Boss of Toys × WooCommerce
Kompleksowa dokumentacja systemu integracji WooCommerce z hurtownią BossOfToys oraz marketplace Allegro, Empik i Ceneo. Napisana na bazie bezpośredniego przeglądu kodu (core/, api/, wordpress-plugin/), a nie tylko starszych notatek — tam, gdzie kod i wcześniejsza dokumentacja się rozjeżdżały, wygrywa kod.
Czym jest ten system?
System Boss of Toys × WooCommerce to zautomatyzowany potok łączący hurtownię, sklep internetowy i kilka marketplace'ów w jeden spójny proces — od produktu w hurtowni, przez sklep i kanały sprzedaży, aż po dostawę do klienta i fakturę.
| Platforma | Rola w systemie |
|---|---|
| BossOfToys (hurtownia) | Źródło produktów, stanów magazynowych i cen (XML lub REST API). Przyjmuje zamówienia dropshippingowe z etykietą PDF. Monitoring pakowania przez skrzynkę e-mail. |
| WooCommerce (sklep) | Sklep internetowy na WordPressie (hosting OVH, domena erotivo.pl). Centralne miejsce zarządzania produktami i zamówieniami niezależnie od kanału sprzedaży. |
| Allegro (marketplace) | Wystawianie ofert, import zamówień, fulfillment, etykiety Shipment Management, wiadomości/reklamacje, faktury, profile cenowe. |
| Empik (marketplace) | Import zamówień przez Mirakl API — OR23 (tracking) i OR24 (potwierdzenie wysyłki). |
| Ceneo Kup Teraz (marketplace) | Zamówienia rozpoznawane po meta _ceneo_order_guid; SetOrderShipment/SendOrder jako odpowiedniki OR23/OR24. Zintegrowane inline w module etykiet i trackingu, bez osobnego CRON-a importu. |
| InPost / ShipX | Generowanie etykiet (paczkomat + kurier), śledzenie paczek, Geowidget (wybór paczkomatu na checkout). |
| Fakturownia.pl | Automatyczne wystawianie i pobieranie faktur PDF, upload do Allegro. |
| OpenAI | Generowanie opisów produktów — osobno dla sklepu (WooCommerce) i osobno pod ograniczenia HTML Allegro. |
System działa w trybie w pełni zautomatyzowanym: od pojawienia się zamówienia (sklep/Allegro/Empik/Ceneo), przez generowanie etykiety, forwarding do hurtowni, śledzenie przesyłki, aż po zamknięcie zamówienia i wystawienie faktury — bez interwencji ręcznej w normalnym przebiegu. Interwencja ręczna (przyciski w panelu WordPress) pozostaje dostępna jako fallback, gdy automat czegoś nie złapie.
Środowisko (potwierdzone 2026-07-18): backend (VPS) działa dziś wyłącznie na Linuksie w kontenerze Docker (docker-compose.yml). Historycznie (do ~v3.32) aplikacja miała też desktopowe GUI (PyQt6) uruchamiane lokalnie na Windows — zostało całkowicie usunięte w v3.33.0. Jeśli natrafisz w starszych plikach .claude/*.md na Windows Server/PowerShell/Windows Firewall — to opis poprzedniego środowiska, zachowany jako zapis historyczny, nieaktualny dla dzisiejszej infrastruktury.
Ta dokumentacja różni się miejscami od poprzedniej wersji nie kosmetycznie, ale merytorycznie — kilka wcześniejszych ustaleń (m.in. o pluginie WordPress i module repricingu) okazało się błędnych po bezpośrednim odczycie kodu/produkcji. Tam, gdzie to istotne, rozdziały niżej wprost zaznaczają "wcześniej sądzono X, w rzeczywistości Y" — czytaj te uwagi uważnie, bo jedna z takich pomyłek doprowadziła 2026-07-18 do realnego incydentu (skasowanie zakładki Allegro na żywym sklepie, opisane w rozdziale Znane problemy i ryzyka, punkt 14).
Architektura ogólna
KLIENCI / KANAŁY SPRZEDAŻY
│
├─ Sklep WooCommerce (erotivo.pl, OVH) ── klient składa zamówienie
├─ Allegro (marketplace) ──────────────── kupujący składa zamówienie
├─ Empik (marketplace, Mirakl) ────────── kupujący składa zamówienie
└─ Ceneo Kup Teraz (marketplace) ──────── kupujący składa zamówienie
│ (wszystkie trafiają jako zamówienia WooCommerce, z meta
│ rozróżniającym pochodzenie — patrz rozdział "Przepływ zamówień")
▼
┌──────────────────────────────────────────────────────────┐
│ WordPress (OVH) — Plugin "BeeIntegro" │
│ (katalog: wordpress-plugin/bossoftoys-manager/) │
│ │
│ Dashboard · Harmonogram · Ustawienia │
│ Boss Of Toys · Allegro (pełny panel, 9+ zakładek) │
│ Logi · Meta box etykiet · Geowidget (checkout) │
└─────────────────┬──────────────────────────────────────────┘
│ HTTP (X-API-Key) ⚠ patrz uwaga niżej — nie HTTPS
▼
┌──────────────────────────────────────────────────────────┐
│ VPS — Python FastAPI (port 8000, Docker) │
│ │
│ /api/jobs/* /api/config/* /api/labels/* │
│ /api/allegro/* /api/sse/* /api/stats/* │
│ /api/categories/* /api/returns/* /api/logs/* │
│ /api/metrics /api/status /health │
│ │
│ SQLite: data/api_bridge.db (WAL mode, 17 tabel) │
└─────────────────┬──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Moduły core/ (47 plików Python, od 2026-07-20) │
│ │
│ 10x BossOfToys/WooCommerce 18x Allegro │
│ 6x Etykiety/logistyka 2x Fakturownia │
│ 1x Ceneo 2x Empik 8x infrastruktura wspólna (+own_stock)│
└─────────────────┬──────────────────────────────────────────┘
│
┌────────────┼────────────┬──────────────┬─────────────┐
▼ ▼ ▼ ▼ ▼
BossOfToys WC REST Allegro ShipX/InPost Fakturownia /
(hurtownia) API OAuth 2.0 REST API Empik / CeneoKomunikacja między komponentami
| Skąd | Dokąd | Protokół / Auth |
|---|---|---|
| WordPress plugin | VPS FastAPI | HTTP (nie HTTPS — patrz uwaga niżej) + nagłówek X-API-Key |
| VPS | WooCommerce REST | consumer_key + consumer_secret |
| VPS | WordPress REST (/wp/v2) | HTTPS + Application Password + Chrome User-Agent (patrz pułapka WAF niżej) |
| VPS | BossOfToys API | email + hasło + akronim klienta |
| VPS | Allegro API | OAuth 2.0 — tokeny w SQLite kv_store |
| VPS | ShipX (InPost) | Bearer token + organization_id |
| VPS | Fakturownia | api_token w URL |
| VPS | Empik (Mirakl) | API Key w nagłówku |
| VPS | Ceneo Kup Teraz | API key → Bearer token (cache TTL ok. 2h) |
| WordPress (zewnętrzny trigger) | VPS Scheduler | HTTP GET z sekretem w query string (eksport/import ofert Empik) — patrz rozdział Harmonogram CRON |
Jak system rozpoznaje zamówienia z różnych kanałów
Nie ma osobnej bazy danych per kanał — wszystko jest zamówieniem WooCommerce. Kanał pochodzenia rozpoznawany jest po obecności konkretnego meta pola (szczegóły w rozdziale Przepływ zamówień i Meta WooCommerce):
| Meta pole | Kanał |
|---|---|
_allegro_checkout_id | Allegro |
_empik_order_id | Empik |
_ceneo_order_guid | Ceneo Kup Teraz |
| (brak powyższych) | Zamówienie sklepowe (bezpośrednio z erotivo.pl) |
Stan pluginu WordPress — sprostowanie po incydencie z 2026-07-18: wcześniejsza wersja tej dokumentacji (i ustalenia robocze z 2026-07-17) twierdziły, że plugin WordPress jest dziś "uproszczony do command center" i że rozbudowany panel Allegro, etykiety, InPost, geowidget nie mają już odpowiednika w plikach pluginu. To było błędne — wynikało z pracy na drastycznie nieaktualnej lokalnej kopii repozytorium (`class-bot-admin.php`: 530 linii lokalnie vs 3833 na produkcji — 7-krotna różnica). Błędne ustalenie doprowadziło do nadpisania i skasowania działającej zakładki Allegro na żywym sklepie. Zweryfikowany stan faktyczny: plugin nazywa się BeeIntegro, ma pełny, rozbudowany panel Allegro (page-allegro.php, 1555 linii) i wszystkie moduły UI (etykiety, InPost, geowidget) w pełni działają. Szczegóły w rozdziale Plugin WordPress.
OVH LiteSpeed WAF blokuje User-Agent python-requests → 403. Wszystkie requesty do /wp-json/wp/v2/* muszą używać USER_AGENT udającego przeglądarkę (Chrome string). Klucze WooCommerce (consumer_key/consumer_secret) NIE działają na WP REST API — potrzebne osobne Application Password.
WordPress → VPS to dziś zwykłe HTTP, nie HTTPS. wordpress-plugin/bossoftoys-manager/includes/class-bot-api.php (linia ok. 65) łączy się z API z jawnie ustawionym 'sslverify' => false, komentarz w kodzie wprost mówi: "Na razie wyłącz weryfikację SSL (HTTP)". Klucz X-API-Key i cała komunikacja (dane zamówień, odpowiedzi z tokenami) lecą bez szyfrowania transportu. Bezpieczeństwo opiera się wyłącznie na warstwie sieciowej. Pełna analiza ryzyka: rozdział Bezpieczeństwo.
Stos technologiczny
Backend (VPS)
| Komponent | Technologia | Plik |
|---|---|---|
| Framework API | FastAPI ≥0.109 + uvicorn ≥0.27 (Python 3.11+) | run_api.py, api/server.py |
| Walidacja/modele | Pydantic ≥2.5 | api/models.py |
| Baza danych | SQLite 3 (WAL mode, foreign_keys ON) | data/api_bridge.db |
| Repository pattern | Własny, 65 metod domenowych (zliczone bezpośrednio w kodzie) | api/repository.py |
| Migracje DB | Własny system wersjonowania (16 migracji) | api/migrations.py |
| Adapter danych | DB z fallbackiem na JSON | core/data_store.py |
| Harmonogram | VPS Scheduler — czysty asyncio, NIE APScheduler | api/scheduler.py |
| Autoryzacja API | Nagłówek X-API-Key | api/auth.py |
| Logi real-time | Server-Sent Events (SSE) | api/routes/sse.py |
| Telemetria HTTP | Middleware + agregacja godzinowa | api/metrics.py |
| WooCommerce SDK | pakiet woocommerce ≥3.0 | core/api_clients_woo.py |
| Parsowanie HTML/XML | BeautifulSoup4 | generatory opisów |
| Obrazy | Pillow (resize/WebP) | product_adder_woo.py i inne |
| Szyfrowanie configu | cryptography (Fernet) | core/secure_config.py |
| fpdf2 + fonty NotoSans (polskie znaki) | core/return_pdf.py, etykiety | |
| AI opisów | OpenAI SDK ≥1.0 (opcjonalny import) | description_generator_woo.py, allegro_description_generator.py |
| Kontener | Docker (obraz bossoftoys-api:latest) | docker-compose.yml |
Pełna lista zależności: requirements.txt w katalogu głównym. W repo jest też starszy, prawdopodobnie nieaktualny .claude/requirements_api.txt — jeśli oba się rozjeżdżają, ufaj requirements.txt (to ten używany realnie przez pip install -r requirements.txt wg CLAUDE.md).
Frontend (WordPress Plugin "BeeIntegro")
| Komponent | Technologia |
|---|---|
| Plugin PHP | WordPress 6.x + WooCommerce 8.x |
| JavaScript | jQuery (vanilla, bez frameworka SPA) — dashboard.js, ok. 10 400 linii |
| Wykresy | Chart.js |
| Ikony | Dashicons (natywne WordPress) |
| Mapa InPost | Geowidget InPost v5 |
| Cache po stronie WP | WordPress Transients, TTL per typ danych (class-bot-cache.php) |
SSE zaimplementowane po stronie backendu i PHP, ale nieużywane w praktyce: api/routes/sse.py i class-bot-api.php mają gotowe endpointy/URL-e Server-Sent Events (w tym klucz API w query stringu URL-a, bo EventSource w przeglądarce nie pozwala na custom headers), ale zweryfikowane bezpośrednio w dashboard.js (10 414 linii) — nie ma tam aktywnego new EventSource(...). Dashboard w praktyce odpytuje przez zwykły AJAX polling (3s przy aktywnym zadaniu, 15s w spoczynku). Jeśli planujesz polegać na SSE dla nowej funkcji, zweryfikuj to jeszcze raz bezpośrednio w kodzie — może się to zmienić.
Backend (VPS) i plugin WordPress mają dwie niezależne numeracje wersji. Wersja aplikacji/backendu to "3.33.0+" (deklarowana w .claude/CLAUDE.md). Wersja pluginu to stała BOT_VERSION w bossoftoys-manager.php (stan 2026-07-20: 3.25.0) — służy też jako ?ver= cache-busting dla dashboard.js/admin.css. Nie próbuj ich zestawiać jako jednej, spójnej wersji systemu — to dwa oddzielne, niezsynchronizowane liczniki.
Wdrożenie i uruchomienie
Projekt działa na dwóch zupełnie oddzielnych infrastrukturach, wdrażanych i aktualizowanych niezależnie. Mylenie tych dwóch procedur jest częstym źródłem "poprawka nie działa po wdrożeniu" — patrz też rozdział Znane problemy, punkt 12.
A) VPS Linux/Docker — backend Python (`api/`, `core/`, `run_api.py`)
Użytkownik wgrywa pliki przez FileZilla/FTP na VPS, gdzie działają w kontenerze Docker.
# Uruchomienie bezpośrednio (bez kontenera, np. dev) pip install -r requirements.txt python run_api.py python run_api.py --port 8080 --reload # --- Produkcja: w Dockerze --- docker compose up -d docker compose logs -f docker compose restart bossoftoys-api # po zmianie pliku .py w api/ lub core/
Kluczowe dla zrozumienia całego procesu wdrożenia: docker-compose.yml montuje jako wolumeny ./data, ./config, ./.env oraz (od 2026-07-18) ./api:/app/api i ./core:/app/core. Wcześniej te dwa ostatnie wolumeny NIE istniały — cały kod Pythona był zaszyty na stałe w obrazie bossoftoys-api:latest, zbudowanym kiedyś, gdzieś indziej (w repo nie ma Dockerfile). Skutek: użytkownik wgrywał poprawione pliki .py przez FTP i restartował kontener, ale te pliki fizycznie nigdy nie docierały do działającego procesu — źródło całej serii pozornie niewyjaśnialnych "poprawka nie zadziałała" w historii tego projektu. Zweryfikuj, że wolumeny api/ i core/ są nadal obecne w docker-compose.yml na VPS, zanim założysz, że wgrany plik .py zacznie działać.
- 1Zmiana pliku
.pywapi/lubcore/Wystarczydocker compose restart bossoftoys-api— wolumeny sprawiają, że kontener widzi nowy plik natychmiast. - 2Zmiana samego
docker-compose.ymlNowe wolumeny, porty, zmienne środowiskowe — wymaga pełnego odtworzenia:docker compose down && docker compose up -d. SamrestartNIE przeładuje nowej konfiguracji. - 3"Update Image" w panelu hostingowym (np. aaPanel) ≠ wgranie koduTo najprawdopodobniej
docker pullobrazu z rejestru — bezużyteczne dlabossoftoys-api:latest, który nigdzie nie jest wypchnięty (brak Dockerfile, brak CI). Nie traktuj tego przycisku jako sposobu wdrożenia kodu. - 4Weryfikacja, że kontener czyta aktualny kod
docker compose exec bossoftoys-api grep -n "unikalny fragment Twojej zmiany" /app/api/plik.py
B) WordPress plugin (`wordpress-plugin/bossoftoys-manager/`) — hosting OVH
Zupełnie osobna infrastruktura, zwykle też FTP, ale bez Dockera i bez restartu — PHP jest interpretowane na żywo.
- Skopiuj folder do
wp-content/plugins/bossoftoys-manager/i aktywuj w WordPress → Wtyczki (przy pierwszej instalacji). - Zmiana pliku
.phpdziała natychmiast po wgraniu — nie ma "restartu"/"kompilacji". - Pułapka cache przeglądarki:
wp_enqueue_script/wp_enqueue_styleużywają stałejBOT_VERSIONjako?ver=. Jeśli nie zbumpujesz tej stałej po zmianiedashboard.js/admin.css, przeglądarki użytkowników mogą serwować starą, zcache'owaną wersję mimo że plik na serwerze jest już nowy. Zawsze rób twardy refresh (Ctrl+Shift+R) po wdrożeniu zmian JS/CSS, zanim ocenisz wynik.
Zanim zrobisz nieaddytywną zmianę w wordpress-plugin/ — zweryfikuj, że lokalna kopia w repozytorium jest aktualna. Ta kopia potrafiła kiedyś być drastycznie nieaktualna (7-krotnie mniejsza niż produkcja) bez żadnego ostrzeżenia, co doprowadziło do realnego incydentu (skasowanie zakładki Allegro na żywym sklepie 2026-07-18, opis w rozdziale Znane problemy punkt 14). Szybki test: wc -l wordpress-plugin/bossoftoys-manager/includes/class-bot-admin.php — spodziewany wynik to ok. 3800 linii; jeśli wynik jest radykalnie mniejszy, kopia jest nieaktualna. Preferuj zmiany addytywne (nowa metoda/funkcja dopisana obok istniejącego kodu) nad przepisywaniem całych plików.
Minimalna konfiguracja `.env`
# WooCommerce WOOCOMMERCE_URL=https://twojsklep.pl WOOCOMMERCE_KEY=ck_xxxxxxxxxxxxxxxxxxxx WOOCOMMERCE_SECRET=cs_xxxxxxxxxxxxxxxxxxxx # WordPress REST API (media, produkty — wymaga Application Password) WP_USERNAME=admin WP_APP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx # BossOfToys BOSSOFTOYS_EMAIL=email@firma.pl BOSSOFTOYS_PASSWORD=haslo BOSSOFTOYS_ACRONYM=ESKL1_XXXX # API security (klucz dla nagłówka X-API-Key) API_SECRET_KEY=bardzo_dlugi_losowy_klucz_min_32_znaki # Allegro OAuth ALLEGRO_CLIENT_ID=twoj_client_id ALLEGRO_CLIENT_SECRET=twoj_client_secret # ShipX (InPost) SHIPX_TOKEN=twoj_token_shipx SHIPX_ORGANIZATION_ID=12345 # Fakturownia FAKTUROWNIA_API_TOKEN=twoj_token FAKTUROWNIA_DOMAIN=twojafirma # Empik Marketplace EMPIK_API_KEY=twoj_klucz_empik EMPIK_SHOP_ID=twoj_shop_id # Ceneo Kup Teraz CENEO_API_KEY=twoj_klucz_ceneo # Opcjonalne (AI, powiadomienia) OPENAI_API_KEY=sk-xxxxxxxxxxxx NTFY_TOPIC=twoj_temat_ntfy SMTP_HOST=smtp.gmail.com SMTP_USER=email@gmail.com SMTP_PASSWORD=app_password ALERT_EMAIL=odbiorca@gmail.com
Migracja na nowy serwer
- 1Skopiuj bazę danychPlik
data/api_bridge.db— cały stan systemu: tokeny OAuth, historia zamówień, cache, profile cenowe. - 2Skopiuj konfiguracjęKatalog
config/+ plik.env. - 3Uruchom serwer
python run_api.py(lubdocker compose up -d) — migracje bazy uruchamiają się automatycznie przy starcie.
Pułapka przy migracji (odkryta przy przejściu Windows→Linux): config/settings.dat jest szyfrowany kluczem wyprowadzonym z adresu MAC maszyny (uuid.getnode() w core/secure_config.py). Skopiowanie tego pliku na sprzęt/kontener z innym adresem MAC powoduje cichy błąd odszyfrowania — kod loguje ostrzeżenie i traktuje config jako pusty, bez crasha. docker-compose.yml świadomie pinuje mac_address: "02:42:ac:11:00:99", żeby przynajmniej kontener był spójny sam ze sobą między restartami. Dane wrażliwe (hasła, klucze API) są bezpieczne niezależnie od tego, bo żyją w .env, nie w settings.dat (SENSITIVE_KEYS są odfiltrowywane przy zapisie) — ryzyko dotyczy tylko ustawień niewrażliwych (marże, limity, flagi modułów). Jeśli po migracji "znikają" jakieś ustawienia bez błędu w logach — to pierwsze podejrzane miejsce.
Pierwsze uruchomienie python run_api.py na czystym środowisku generuje i jednorazowo wypisuje w konsoli nowy API_SECRET_KEY, jeśli żaden nie jest ustawiony — trzeba go wtedy skopiować do ustawień pluginu WordPress, bo nie zostanie wyświetlony ponownie.
Konfiguracja
Priorytet ładowania (pierwszy wygrywa)
- Zmienne środowiskowe procesu (
export WOOCOMMERCE_URL=...) - Plik
.envw katalogu głównym - Plik
config/settings.json(tryb serwerowy — obecny) - Plik
config/settings.dat(zaszyfrowany, relikt trybu Desktop — patrz pułapka MAC wyżej)
ConfigManager (core/secure_config.py) cache'uje config w pamięci procesu. Po zapisie configu przez API wywoływana jest inwalidacja cache'a automatycznie — nie trzeba tego robić ręcznie z zewnątrz.
Sekcje konfiguracji
| Sekcja | Zawartość |
|---|---|
woocommerce | URL, klucze API, WP credentials (Application Password) |
bossoftoys | Dane logowania, tryb danych (api/xml), marże per kategoria |
allegro | OAuth (client_id/secret), min_stock, price_margin, GPSR, dostawa, pickup, kategorie |
boss_api | auto_forward, dane logowania do forwardingu |
shipx | Token ShipX, organization_id |
fakturownia | Token API, domena |
empik | API key, shop_id |
ceneo | API key (Ceneo Kup Teraz) |
ntfy | Topic powiadomień push (zwykłe + alerts_topic dla krytycznych) |
smtp | Konfiguracja e-mail dla alertów |
openai | Klucz API OpenAI (opisy AI) |
Harmonogram VPS — plik `config/scheduler.json`
Struktura pliku ma dwie części: jobs (moduły uruchamiane cyklicznie przez VPS Scheduler) i http_triggers (zewnętrzne wywołania HTTP GET do WordPressa, głównie dla Empika). Pełna, aktualna zawartość jobs — patrz rozdział Harmonogram CRON.
Sekcja http_triggers w config/scheduler.json zawiera żywe URL-e z sekretem uwierzytelniającym w query string (parametr secret=..., wywołania action=export_offers/import_orders/export_products na erotivo.pl dla Empika). Nie kopiuj tych URL-i do żadnej dokumentacji, logu, PR-a ani zgłoszenia — traktuj je jak hasło.
VPS Scheduler (api/scheduler.py, czysty asyncio, niezależny od WP-Cron i od APScheduler) zastępuje WP-Cron dla wszystkich modułów cyklicznych. WP pseudo-cron działa tylko przy odwiedzinach strony, więc nie nadaje się do niezawodnego automatyzowania — w panelu WordPress warto mieć WP-Cron wyłączony dla zadań, które przejął scheduler VPS. Zmiana w scheduler.json jest podchwytywana automatycznie, bez restartu procesu.
Baza danych SQLite
Plik: data/api_bridge.db — WAL mode + PRAGMA foreign_keys=ON. Cały trwały stan aplikacji żyje w jednym pliku.
Architektura dostępu do danych
core/*.py
└─ from core.data_store import data_store (import LOKALNY, wewnątrz funkcji!)
└─ core/data_store.py (adapter)
├─ tryb serwerowy (VPS): api/repository.py → api/database.py → SQLite
└─ fallback: pliki JSON (relikt trybu Desktop, dziś praktycznie nieużywany)Zasada krytyczna, świadoma konwencja projektu — nie "napraw" jej: moduły w core/ nigdy nie importują api.repository bezpośrednio (circular import przez Pydantic). Zawsze przez core.data_store, i zawsze lokalnie, wewnątrz funkcji (from core.data_store import data_store na początku funkcji, nie na górze pliku).
17 tabel bazy danych (stan zweryfikowany bezpośrednim zapytaniem SQL do żywej data/api_bridge.db, 2026-07-18)
| Tabela | Zawartość | Retencja |
|---|---|---|
jobs | Historia uruchomień modułów (tabela bazowa, tworzona w api/database.py, nie w migracjach) | 7 dni |
job_logs | Logi z wykonań zadań (tabela bazowa) | 7 dni (CASCADE) |
schema_migrations | Wersje zastosowanych migracji DB | Trwałe |
kv_store | Klucz-wartość: tokeny OAuth Allegro, checkpointy, cursory eventów | Trwałe dla tokenów |
allegro_synced_orders | Historia sync zamówień Allegro → WC (deduplikacja) | Trwałe (nigdy nie kasowane) |
forwarded_orders | Zamówienia przekazane do BossOfToys (deduplikacja) | Trwałe (nigdy nie kasowane) |
job_results | Wyniki zadań (JSON) | 7 dni (CASCADE) |
cache_entries | Cache SKU, EAN, stany magazynowe, dopasowania katalogowe | TTL per wpis |
allegro_events | Zdarzenia z Allegro Events API | Max 500 wpisów |
allegro_fulfillment_state | Stan fulfillmentu ofert Allegro | Trwałe |
ghost_tracker | Daty nieobecności produktów u dostawcy (Stock Sync, Product Deleter) | Aktywne |
run_stats | Statystyki uruchomień modułów | Aktywne |
metrics_hourly | Metryki HTTP API (agregacja godzinowa) | 48h |
returned_shipments | Zwroty przesyłek (moduł Zwrotów, api/routes/returns.py) | Trwałe |
return_notes | Notatki do zwrotów + meta kolumny returned_shipments (migracja 13) | Trwałe |
markup_profiles | Profile narzutów Allegro — nazwane zestawy marż per kategoria WC | Trwałe |
tier_profiles | Profile przedziałów cenowych Allegro — progi cena_hurtowa→narzut% per kategoria, w tym kolumna tier_tables (JSON, dodana migracją 16 — patrz uwaga niżej) | Trwałe |
tier_tables to KOLUMNA, nie osobna tabela — nazwa migracji #16 w rejestrze (api/migrations.py::MIGRATIONS) jest myląca i sugeruje nową tabelę, ale w rzeczywistości to ALTER TABLE tier_profiles ADD COLUMN tier_tables TEXT NOT NULL DEFAULT '[]' + backfill grupujący istniejące category_tiers o identycznej treści w nazwane, współdzielone zestawy. Potwierdzone zarówno odczytem _migration_016_tier_tables(), jak i żywym zapytaniem SELECT name FROM sqlite_master WHERE type='table' do produkcyjnej bazy — wynik to 17 tabel aplikacji (plus wewnętrzna, autogenerowana sqlite_sequence, która nie jest tabelą aplikacji).
Liczba tabel/migracji rośnie z czasem (16 migracji na 2026-07-18, wcześniej dokumentacja podawała "13 tabel" — to był stan sprzed migracji 12-16, dodających zwroty i profile cenowe). Przed dodaniem nowej migracji zawsze sprawdź faktyczny koniec listy MIGRATIONS w api/migrations.py — plan multi-account Allegro (.claude/plan_multi_account_allegro.md) zakłada np. że wolny numer to #12, co jest już nieaktualne (#12 to od dawna returned_shipments). Użyj len(MIGRATIONS) + 1, nie numeru wpisanego w starym dokumencie.
Profile narzutów i przedziałów cenowych (nowość — `core/allegro_markup_profiles.py`)
Dwa niezależne mechanizmy cenowe, oba z dopasowaniem kategorii WooCommerce po prawdziwej hierarchii ID (rodzic/dziecko), nie po dopasowaniu tekstowym nazw:
- Profile narzutów (
markup_profiles) — marża jako mnożnik ceny WC. Używane przezallegro_offer_sync.py(run),allegro_product_lister.py,allegro_scanner.py. - Profile przedziałów cenowych (
tier_profiles+tier_tables) — progi cena_hurtowa → narzut %, per kategoria. Używane przezallegro_offer_sync.py(run_apply_suggestions).
Gdy żaden profil danego typu nie jest aktywny, zachowanie jest w 100% zgodne z poprzednim, prostszym mechanizmem marż — zero ryzyka dla instalacji, które nie korzystają z tej funkcji.
Migracje uruchamiają się automatycznie przy starcie serwera (run_migrations() wołane w lifespan() serwera FastAPI) — nie wymagają ręcznej obsługi.
Moduły — BossOfToys / WooCommerce
kv_store). Ghost tracking — produkty nieobecne przez X dni są zerowane.ftp_uploader.py). Obsługa checkpoint (resume po przerwaniu)._cost_price.<!-- AI_DESC:YYYY-MM-DD -->. Backup oryginałów, resume, retry 3×. Osobny moduł od generatora pod Allegro (patrz niżej).forwarded_orders. Pomija zamówienia w 100% z "własnego towaru" (patrz niżej).run(), nie job) — flaga _own_stock_item na produkcie. Używana przez Order Forwarder i Label Auto Generator, żeby pomijać forward zamówień ze zwrotami sprzedawanymi wyłącznie w sklepie.api_clients_woo.py). Biblioteka, nie samodzielny job.system@boysoftoys.pl z numerami zamówień (wzorzec ZS-XXXXX/XX/XXXX) i oznacza odpowiednie zamówienia WC jako spakowane. Meta: _boss_packed_at.Szczegóły modułu Order Forwarder
Order Forwarder sprawdza etykietę PDF PRZED tworzeniem zamówienia BossOfToys. Jeśli etykieta nie istnieje — zamówienie jest pomijane i moduł spróbuje ponownie w następnym cyklu CRON. Nigdy nie duplikuje zamówień dzięki tabeli forwarded_orders.
Tryb auto_forward: jeśli boss_api.auto_forward=true, forwarding odbywa się inline po wygenerowaniu etykiety w label_auto_generator.py — bez potrzeby osobnego CRON-a Order Forwarder. Produkcyjny config/scheduler.json ma mimo to Order Forwarder włączony (co 15 min) jako niezależny mechanizm nadrabiający zamówienia, które z jakiegoś powodu ominęły ścieżkę auto-forward.
Własny towar / zwroty — moduł "Own Stock" (own_stock.py, nowość 2026-07-20)
Scenariusz biznesowy: klient odsyła oryginalnie zapakowany zwrot, sklep chce go odsprzedać wyłącznie we własnym sklepie (nie na Allegro). Produkt jest duplikowany natywną funkcją WooCommerce "Duplikuj", dostaje sztuczny SKU i EAN (nigdy nieobecne u dostawcy BossOfToys), i jest wystawiany z realną ilością posiadaną fizycznie.
- 1Checkbox na karcie produktuZakładka Zapasy,
class-bot-own-stock.php→ meta_own_stock_item = yes. Ilość sztuk pilnuje natywny WooCommerce stock (_manage_stock/_stock) — świadomie brak własnego licznika ilości. - 2order_is_all_own_stock()Sprawdzana w
order_forwarder_woo.pyi inline wlabel_auto_generator.py: True tylko gdy WSZYSTKIE pozycje zamówienia mają flagę. Jeśli choć jedna pozycja nie jest własnym towarem — całe zamówienie idzie do BossOfToys normalnie (bez rozdzielania pozycji). - 3DedupZamówienie oznaczane meta
_own_stock_handled = 1po pierwszym rozpoznaniu — kolejne cykle CRON nie odpytują ponownie WC o meta produktów. - 4Ochrona przed skasowaniem
product_deleter_woo.pywyklucza produkty z_own_stock_item=yesz ghost-trackingu — bez tego sztuczny SKU (nigdy nieobecny u dostawcy) zostałby po czasie usunięty razem ze zdjęciami. - 5Widoczność dla sprzedawcyNTFY push ("Wyslij TY! Zamowienie #X") + badge 🏠 "Wysyłasz Ty" na liście zamówień WC (
class-bot-order-list.php) + ostrzeżenie w meta boxie na stronie zamówienia (class-bot-order-meta.php) — trzy niezależne kanały. - 6Filtr na liście produktówDropdown "Tylko własny towar" nad listą produktów WP (nowość 2026-07-21,
class-bot-own-stock.php) — obok badge w kolumnie pozwala też odfiltrować widok do samych produktów oznaczonych_own_stock_item.
Przy duplikacji produktu pamiętaj o polu EAN — WC "Duplikuj" nie czyści go automatycznie. Projekt ma dwa miejsca przechowywania EAN: natywne global_unique_id (pole "GTIN, UPC, EAN lub ISBN" w zakładce Zapasy) oraz legacy custom meta _gtin (czytane jako pierwsze przez allegro_scanner.py przy dopasowywaniu do katalogu Allegro). Oba trzeba wyczyścić/zmienić, inaczej duplikat może się dopasować do prawdziwej oferty Allegro po EAN mimo że nigdy nie ma tam trafić.
Zastąpiony i usunięty mechanizm home_stock_qty: wcześniejsza, ilościowa wersja tej funkcji (pole dodające się do stanu z hurtowni) została całkowicie usunięta 2026-07-20 — nie miała UI w WordPressie, rejestr w produkcyjnej bazie był pusty (nieużywany), i miała realną lukę bezpieczeństwa (brak wykluczenia w product_deleter_woo.py). Jeśli natrafisz na wzmiankę o home_stock_qty/HOME_STOCK_META w starszych notatkach — to relikt.
Znalezisko przy odczycie kodu: plik core/boss_auto_forwarder.py ("Automatyczne przekazywanie zamówień do BossOfToys") istnieje w core/, ale nie jest importowany z żadnego innego pliku .py w repo (ani api/, ani inny moduł core/) — sprawdzone bezpośrednim grepem. Wygląda na kod zastąpiony przez order_forwarder_woo.py + flagę auto_forward, pozostawiony w drzewie bez usunięcia. Zanim go dotkniesz lub usuniesz, potwierdź z użytkownikiem, że rzeczywiście jest martwy — grep nie wyklapie dynamicznego importu, gdyby taki gdzieś istniał.
Moduły — Allegro Marketplace
18 plików w core/allegro_*.py — największy pojedynczy obszar integracji w projekcie. Wspólny klient (allegro_client.py) obsługuje OAuth 2.0 i dziesiątki metod REST; api/routes/allegro.py wystawia ok. 106 endpointów HTTP na bazie tych modułów.
| # | Moduł | Plik | Opis |
|---|---|---|---|
| 1 | Allegro Client | allegro_client.py | Klient API Allegro z OAuth 2.0. Tokeny w SQLite kv_store. Fundament dla wszystkich pozostałych modułów Allegro. |
| 2 | Allegro Scanner | allegro_scanner.py | Skanuje produkty WC → dopasowanie do katalogu Allegro po EAN/GTIN. Cache TTL 7 dni. Respektuje checkbox _allegro_exclude (nowość 2026-07-22) — produkty z tą flagą nigdy nie trafiają do listy dopasowanych, nawet z prawdziwym EAN. |
| 3 | Allegro Lister | allegro_product_lister.py | Wystawia oferty. Smart zdjęcia (deduplikacja URL), HTML→sekcje Allegro, GPSR, kategoria overrides, retry parametrów po 422. |
| 4 | Allegro Order Sync | allegro_order_sync.py | Importuje zamówienia Allegro → WC. Ustawia attribution "Allegro" (drugi PUT po zapisie). Deduplikacja przez allegro_synced_orders. |
| 5 | Allegro Offer Sync | allegro_offer_sync.py | Synchronizuje stany/ceny WC → oferty Allegro. Kończy oferty przy zerowym stanie, reaktywuje po powrocie. Obsługuje profile narzutów/przedziałów cenowych. |
| 6 | Allegro Fulfillment | allegro_fulfillment_sync.py | Statusy WC → Allegro (completed → SENT). Wysyła numer trackingu. |
| 7 | Allegro Messaging | allegro_messaging_sync.py | Autoresponder wiadomości od kupujących. Uruchamiany co 5 min. |
| 8 | Allegro Events | allegro_events_poller.py | Poller zdarzeń Allegro. Max 500 zdarzeń w DB. Cursor trzymany w kv_store. |
| 9 | Allegro Cost Backfill | allegro_cost_backfill.py | Uzupełnia _cost_price z BossOfToys — dane wejściowe do modułu rentowności. |
| 10 | Allegro Category Sync | allegro_category_sync.py | Synchronizacja kategorii Allegro → WooCommerce (tryby preview i sync). Ostrożnie — run() (nie run_preview()) CAŁKOWICIE nadpisuje pole categories produktów wystawionych na Allegro kategorią z taksonomii Allegro, gubiąc kategorię z hurtowni. Spowodowało to realny incydent 2026-07-22 (11 475 produktów ze złą kategorią) — patrz rozdział Znane problemy. |
| 11 | Allegro Params Sync | allegro_params_sync.py | Sync parametrów ofert Allegro → atrybuty WC (tryby preview i sync). |
| 12 | Allegro Label Generator | allegro_label_generator.py | 3-krokowy flow etykiet Shipment Management (patrz rozdział Etykiety). Wywoływany inline przez label_auto_generator.py, nie jako osobny job. |
| 13 | Allegro Pickup Scheduler | allegro_pickup_scheduler.py | Automatyczne zamawianie podjazdu kuriera. Per-przewoźnik: InPost/GLS/UPS skip, DPD optional, DHL must_schedule. |
| 14 | Allegro Health Monitor | allegro_health_monitor.py | CRON co 60 min. Sprawdza ważność tokenu OAuth i odpowiedź API. Przy rozłączeniu wysyła krytyczne NTFY (z 2h cooldownem, żeby nie zalać powiadomieniami). |
| 15 | Allegro Markup Profiles | allegro_markup_profiles.py | Profile narzutów i przedziałów cenowych — patrz opis w rozdziale Baza danych. |
| 16 | Allegro Description Generator | allegro_description_generator.py | Generator opisów AI pod ograniczenia HTML Allegro (dozwolone <p> <b> <i> <u> <ul> <ol> <li> <h1-3>; zabronione m.in. <br> <strong> <em> <img> <a>). Osobny model/logika od generatora sklepowego. |
| 17 | Issues (dyskusje/reklamacje) | w allegro_client.py | API beta.v1 (GET /sale/issues) — GET /sale/disputes zostało wycofane przez Allegro 2026-01-07. |
| 18 | Allegro Invoice Upload | allegro_invoice_upload.py | Prawdopodobnie martwy kod — patrz uwaga niżej. |
Dwa znaleziska "martwego kodu" przy weryfikacji tego rozdziału:
core/allegro_repricing.py— funkcjonalnie usunięty. Cała treść pliku to dziśdef run(*args, **kwargs): raise RuntimeError(...)z komunikatem "Allegro zablokowało dostęp do API cen konkurencji na kontach produkcyjnych". Jeśli w panelu WordPress lub w starszych notatkach natrafisz na wzmiankę o module Repricing jako działającej funkcji analizy konkurencji — to nieaktualne. EndpointPOST /api/allegro/jobs/repricingmoże nadal istnieć wapi/routes/allegro.py, ale wywoła ten sam błąd.core/allegro_invoice_upload.py("Moduł do wysyłania faktur do Allegro") — funkcjaupload_invoice_to_allegro()nie jest importowana z żadnego innego pliku.pyw repo. Realny upload faktur do Allegro (przycisk "Wyślij fakturę" w panelu WP) idzie inną drogą:class-bot-admin.php::ajax_allegro_upload_invoice()woła bezpośrednioPOST /api/allegro/orders/{id}/invoices, endpoint zaimplementowany wprost wapi/routes/allegro.py, bez użycia tego modułu.
Ważne gotcha — Allegro API (raz odkryte, nie odkrywaj drugi raz)
| Problem | Poprawne rozwiązanie |
|---|---|
| Pobieranie etykiety PDF | Dwa różne wywołania w realnym kodzie (allegro_client.py), łatwo je pomylić: POST /shipment-management/label wysyła Accept: application/octet-stream i zwykle dostaje PDF bezpośrednio w odpowiedzi (fallback: JSON z labelId, jeśli PDF nie jest gotowy od razu); dopiero gdy trzeba dociągnąć PDF osobno przez GET /shipment-management/label/{labelId}, nagłówkiem jest Accept: application/pdf (nie octet-stream). Zweryfikowane bezpośrednio w kodzie 2026-07-18 — starsze notatki projektu podawały tylko jeden z tych dwóch wariantów. |
| Status przy tworzeniu oferty | HTTP 202 (Accepted) to sukces — _make_request() musi akceptować [200, 201, 202, 204] |
Brak dryRun przy tworzeniu ofert | POST /sale/product-offers?dryRun=true ignoruje parametr po cichu i tworzy prawdziwą ofertę. Jedyny endpoint z realnym wsparciem dryRun to PUT /sale/product-offers/{id} (edycja). |
| Opisy — niedozwolone tagi | <br> niedozwolony, zamień na </p><p>. Dozwolone inline: <b>, <i>, <u>. Zabronione: <img>, <a>, <span>, <div>, <table> |
| Zdjęcia w ofercie | Tablica URL-i: ["https://..."], NIE obiektów [{"url": "..."}]. WooCommerce potrafi zwracać zduplikowane zdjęcia (główne + galeria) — trzeba deduplikować. |
| GPSR payload | Zagnieżdżona struktura: producerData.tradeName, producerData.address.postalCode (nie zipCode), producerData.contact.phoneNumber (nie phone) |
| Parametry kategorii przy wystawianiu | Produkty pod istniejącym katalogiem Allegro DZIEDZICZĄ parametry — wysłanie ich w payloadzie oferty daje 422. Wzorzec: wyślij ofertę BEZ parametrów, jeśli Allegro zwróci MissingRequiredParameters → dociągnij brakujące z katalogu i spróbuj ponownie. |
| Limity | /messaging/threads i .../messages — max limit=20 (422 powyżej). /orders — limit do 1000. |
| Sprawdzanie autoryzacji Allegro | Sprawdzaj client_id + client_secret — NIE access_token (tokeny są w SQLite, nie w configu) |
| Tracking status | statuses[-1] (OSTATNI = najnowszy), odpowiedź API jest chronologiczna |
| Endpoint etykiety | /shipment-management/label/commands NIE ISTNIEJE (404). Używaj POST /shipment-management/label |
| Sandbox ≠ produkcja | Braki uprawnień/danych katalogowych na sandboxie nie muszą występować na produkcji i odwrotnie — nie diagnozuj środowiska produkcyjnego na podstawie zachowania sandboxa bez potwierdzenia. |
Mapowanie statusów fulfillment
| WooCommerce | Allegro |
|---|---|
| processing / on-hold | PROCESSING |
| completed | SENT |
| cancelled / refunded / failed | CANCELLED |
Allegro nie ma statusu "DELIVERED" dla kurierów w kontekście fulfillmentu — SENT jest statusem końcowym pozytywnym. Status faktycznej dostawy śledzony jest osobno przez Shipment Management API (patrz rozdział Etykiety).
Ręczne wykluczenie produktu z Allegro (_allegro_exclude, checkbox na karcie produktu, nowość 2026-07-22): sprawdzane wyłącznie w allegro_scanner.py (zero kosztu, meta_data już pobierane per produkt) — wykluczony produkt nigdy nie trafia do listy dopasowanych. Świadomie NIE sprawdzane w allegro_product_lister.py (w przeciwieństwie do sku_blacklist, sprawdzanego tam drugi raz jako zabezpieczenie przed nieświeżym preview) — Lister nie ma dziś żadnej zależności od WooCommerceClient, działa wyłącznie na zapisanym snapshocie ze skanowania. Jeśli zaznaczysz checkbox PO skanowaniu, ale wystawisz z tego samego (już nieaktualnego) preview — produkt może się jednak wystawić. Zawsze skanuj ponownie przed wystawianiem, jeśli mogłeś zmienić ten checkbox w międzyczasie.
Multi-konto Allegro to plan, nie rzeczywistość. .claude/plan_multi_account_allegro.md (2026-06-11) opisuje 10 błędów architektonicznych blokujących dodanie drugiego konta i proponuje naprawę fazową — status "gotowy do implementacji", ale nic z planu nie zostało wdrożone (zero odniesień do account_name w allegro_client.py). Kod dziś zakłada jedno, hardkodowane konto (erotivo_pl). Nie projektuj zmian tak, jakby wielokontowość już istniała.
Moduły — Etykiety i Wysyłka
Label Auto Generator (CRON) — centralny moduł automatyzacji wysyłki
Plik: core/label_auto_generator.py.
Zamówienie "processing" bez etykiety
│
├─ _allegro_checkout_id JEST → Allegro Shipment Management API
└─ _allegro_checkout_id BRAK → ShipX (InPost) API
Po wygenerowaniu etykiety:
├─ Zapis PDF: data/labels/label_{order_id}.pdf
├─ Meta WC: _*_shipment_id, _*_tracking_number, _shipping_label_source
├─ Ceneo: jeśli zamówienie ma _ceneo_order_guid → SetOrderShipment (tracking)
├─ Empik: jeśli zamówienie ma _empik_order_id → OR23 (tracking) inline
├─ Pickup Scheduling (Allegro, inline, non-fatal błędy)
├─ Auto-forward do BossOfToys (jeśli auto_forward=true)
└─ NTFY/e-mail przy błędach3-krokowy flow Allegro (`allegro_label_generator.py`)
- 1POST /shipment-management/shipments/create-commandsTworzy przesyłkę z
additionalProperties(np. InPostsendingMethod) i smart wymiarami paczki - 2GET /shipment-management/shipments/create-commands/{id}Polling co 2s, max 30 prób — czeka na potwierdzenie
- 3aPOST /shipment-management/labelPayload:
{"shipmentIds": [...]}→ zwraca labelId - 3bGET /shipment-management/label/{labelId}Header:
Accept: application/pdf— zwraca bajty PDF (używane, gdy krok 3a nie zwrócił PDF-a bezpośrednio; sam krok 3a wołaAccept: application/octet-stream— patrz uwaga w tabeli pułapek Allegro wyżej)
Smart wymiary paczek InPost
| Rozmiar | Wymiary | Max waga | Meta produktu |
|---|---|---|---|
| A | 64 × 38 × 8 cm | 5 kg | _inpost_parcel_size = a |
| B | 64 × 38 × 19 cm | 10 kg | _inpost_parcel_size = b |
| C | 64 × 38 × 41 cm | 25 kg | _inpost_parcel_size = c |
| Kurier | wymusza kuriera | — | _inpost_parcel_size = courier |
System bierze największy gabaryt ze wszystkich produktów w zamówieniu. PHP hook (class-bot-inpost-sizes.php) kopiuje meta z produktu do line item przy składaniu zamówienia.
Pickup Scheduling (`allegro_pickup_scheduler.py`)
| Kurier | Zachowanie | Powód |
|---|---|---|
| InPost | skip | BossOfToys ma umowę (codzienny odbiór) |
| GLS | skip | BossOfToys ma umowę |
| UPS | skip | BossOfToys ma umowę |
| DPD | optional | Ustna umowa, konfigurowalne |
| DHL | must_schedule | Brak umowy — zawsze scheduluj |
Shipment Tracking (`shipment_tracking.py`)
CRON co 120 min. Trzy źródła danych równolegle:
| Źródło | Dotyczy | Kluczowy status → akcja |
|---|---|---|
| ShipX API | zamówienia z _shipx_shipment_id | collected_from_sender → WC auto-complete |
| Allegro Tracking API | zamówienia z _allegro_shipment_id | IN_TRANSIT → WC shipped, DELIVERED → WC completed → Fakturownia → upload faktury do Allegro → Fulfillment sync SENT |
| Ceneo (inline, ten sam moduł) | zamówienia z _ceneo_order_guid | przy statusie collected_from_sender i braku _ceneo_send_order_sent → SendOrder (odpowiednik OR24) |
Zwroty przesyłek — moduł "Zwroty" (`return_pdf.py`, `api/routes/returns.py`)
Osobny, mniej udokumentowany w starszych materiałach podsystem: rejestruje zwroty/nieodebrane paczki w tabelach returned_shipments i return_notes, wystawia 5 endpointów w api/routes/returns.py. core/return_pdf.py generuje zbiorczy PDF ze zwrotami (fpdf2 + fonty NotoSans dla polskich znaków) jako dowód dla hurtowni BossOfToys. Wspierające skrypty w scripts/: backfill_returns.py, import_real_return.py, seed_test_return.py.
Moduły — Integracje zewnętrzne
Fakturownia.pl
Plik: core/fakturownia_client.py, core/fakturownia_lump_sum_fix.py.
- Produkty: Product Adder tworzy kartotekę (SKU, nazwa, cena, EAN) przy dodawaniu produktu.
- Faktury: PHP hook planuje pobranie PDF 60s po zmianie statusu zamówienia na
processing. - Pliki:
wp-content/uploads/faktury/faktura_{order_id}.pdf. Auto-cleanup po 3 miesiącach. - Jeśli zamówienie Allegro: faktura uploadowana do Allegro przez
POST /api/allegro/orders/{id}/invoices(patrz uwaga o martwym moduleallegro_invoice_upload.pyw rozdziale Allegro). - Lump Sum Fix — CRON dzienny (
days_back=7) korygujący faktury przy rozliczeniu ryczałtowym.
Empik Marketplace — OR23 i OR24 (Mirakl API)
Plik: core/empik_client.py, core/empik_price_sync.py.
| Operacja | Co robi | Kiedy | Meta WC |
|---|---|---|---|
| OR23 | Wysyła numer trackingu do Empika | Inline po wygenerowaniu etykiety | _empik_carrier_tracking_number |
| OR24 | Potwierdza wysyłkę → Empik SHIPPED | CRON shipment-tracking gdy kurier odebrał | _empik_or24_sent = "1" |
Warunek automatycznego OR24: _empik_order_id JEST + _empik_carrier_tracking_number JEST + _empik_or24_sent BRAK + status WC = collected_from_sender LUB shipped/completed.
Jeśli CRON nie wyśle OR24 automatycznie, dostępny jest ręczny przycisk "Wyślij OR24" w meta boxie etykiety na zamówieniu WooCommerce (fallback świadomie zaprojektowany na wypadek race condition — patrz rozdział Znane problemy).
Import/eksport ofert i zamówień Empik odbywa się przez http_triggers w config/scheduler.json — VPS Scheduler cyklicznie odpytuje URL-e na erotivo.pl (action=export_offers, import_orders, export_products) zabezpieczone sekretem w query string, nie przez bezpośrednie wywołanie Pythona.
Ceneo Kup Teraz — integracja bez osobnego CRON-a
Plik: core/ceneo_client.py (klasa CeneoClient, BasketService + AuthorizationService).
| Metoda klienta | Rola | Odpowiednik |
|---|---|---|
ConfirmOrder | Potwierdzenie przyjęcia zamówienia | — |
SetOrders | Powiązanie zamówienia Ceneo z ID zamówienia WC | — |
SetOrderShipment | Wysłanie numeru trackingu — wołane inline z label_auto_generator.py | odpowiednik Empik OR23 |
SendOrder | Potwierdzenie wysyłki — wołane inline z shipment_tracking.py | odpowiednik Empik OR24 |
W przeciwieństwie do Empika i Allegro, Ceneo nie ma własnego modułu importu zamówień ani osobnego wpisu w ModuleType/MODULE_MAP — logika jest wpleciona bezpośrednio w label_auto_generator.py i shipment_tracking.py, sterowana obecnością meta _ceneo_order_guid na zamówieniu WC. Token uwierzytelniający cache'owany z TTL ok. 2h (odświeżanie z zapasem 200s przed wygaśnięciem 7200s).
OpenAI — generowanie opisów
Dwa niezależne generatory, bo mają różne ograniczenia formatu docelowego:
description_generator_woo.py— opisy sklepowe WooCommerce, HTML z inline stylami, modele gpt-4o-mini/gpt-4o.allegro_description_generator.py— opisy pod restrykcyjny zestaw dozwolonych tagów HTML Allegro (patrz rozdział Allegro).
Plugin WordPress — "BeeIntegro"
Ten rozdział opisuje zweryfikowany stan faktyczny z 2026-07-18, po incydencie opisanym w rozdziale Znane problemy (punkt 14). Wcześniejsze wersje dokumentacji projektu twierdziły, że ten plugin jest "uproszczony do command center" bez UI Allegro/etykiet/InPost/geowidgetu — to było błędne ustalenie oparte na nieaktualnej lokalnej kopii repozytorium, nie na stanie produkcyjnym. Zawsze zweryfikuj rozmiar kluczowych plików przed edycją (patrz na końcu tego rozdziału) — lokalna kopia może się ponownie zdezaktualizować.
Plugin w interfejsie WordPress nazywa się BeeIntegro (nie "BossOfToys Manager" — to nazwa historyczna/wewnętrzna katalogu). Deployowany osobno od VPS/Dockera, zwykle FTP bezpośrednio do wp-content/plugins/bossoftoys-manager/ na hostingu OVH.
Struktura plików (stan 2026-07-20)
bossoftoys-manager/
├── bossoftoys-manager.php # główny plik, require_once x11, stała BOT_VERSION (3.25.0)
├── readme.txt
├── includes/
│ ├── class-bot-api.php (415 linii) — komunikacja z VPS API (BOT_API)
│ ├── class-bot-admin.php (3833 linie!) — menu, WSZYSTKIE 134 AJAX handlery,
│ │ w tym cały backend UI Allegro
│ ├── class-bot-cron.php (572 linie) — WP-Cron (niezależny od VPS schedulera)
│ ├── class-bot-cache.php (277 linii) — cache WP Transients (BOT_Cache, TTL/typ)
│ ├── class-bot-allegro-orders.php (385 linii) — integracja zamówień Allegro z WC Orders
│ ├── class-bot-labels.php (1062 linie) — meta box etykiet na zamówieniu WC
│ ├── class-bot-geowidget.php (366 linii) — widget wyboru paczkomatu na checkout
│ ├── class-bot-inpost-sizes.php (325 linii) — gabaryty InPost per produkt
│ ├── class-bot-own-stock.php (99 linii) — NOWOŚĆ 2026-07-20: checkbox "własny towar"
│ │ (zwroty) w zakładce Zapasy + kolumna produktów
│ ├── class-bot-order-meta.php (222 linie) — meta boxy (Ceneo GUID, InPost) + ostrzeżenie
│ │ "wysyłasz Ty" (2026-07-20)
│ ├── class-bot-order-list.php (248 linii) — kolumny/filtry na liście zamówień WC + kolumna
│ │ "Własny towar" (badge 🏠, 2026-07-20)
│ └── class-bot-ntfy.php (130 linii) — powiadomienia NTFY z poziomu WP
├── admin/
│ ├── views/
│ │ ├── dashboard.php (603 linie) — Dashboard ("BeeIntegro", bot-dashboard-4)
│ │ ├── page-allegro.php (1555 linii!) — CAŁY panel Allegro (multi-tab)
│ │ ├── page-bossoftoys.php (661 linii) — panel hurtowni Boss Of Toys
│ │ ├── page-logs.php (106 linii) — przegląd logów systemowych
│ │ ├── schedule.php (338 linii) — harmonogram (UI WP-Cron + VPS scheduler)
│ │ └── settings.php (709 linii) — ustawienia (WooCommerce, BossOfToys, Allegro…)
│ ├── js/dashboard.js (10414 linii!) — CAŁA logika JS (AJAX, wykresy, polling)
│ └── css/admin.css (7285 linii) — style
└── assets/
├── img/logo.png
├── css/geowidget.css
└── js/geowidget.jsMenu w panelu WP
Top-level: BeeIntegro. Submenu (6 stron, każda osobny plik w admin/views/): Dashboard, Harmonogram, Ustawienia, Boss Of Toys, Allegro, Logi.
Wzorzec AJAX (do naśladowania przy nowych funkcjach)
- 1class-bot-api.phpMetoda wrapper wołająca
$this->request('/api/...')(wzorzec:get_stats(),get_modules_list()) - 2class-bot-admin.php :: __construct()
add_action('wp_ajax_bot_nazwa_akcji', array($this, 'ajax_nazwa_akcji')); - 3class-bot-admin.php :: ajax_nazwa_akcji()
check_ajax_referer('bot_ajax_nonce', 'nonce')→current_user_can('manage_woocommerce')→ wywołanieBOT()->api->...()→wp_send_json_success/error - 4dashboard.js (lub plik JS danej strony)
$.ajax({url: botData.ajaxUrl, method: 'POST', data: {action: 'bot_nazwa_akcji', nonce: botData.nonce, ...}})
Dokładnie ten wzorzec posłużył do dodania funkcji "Podsumowanie błędów" 2026-07-18 (bot_get_error_digest → ajax_get_error_digest() → BOT_API::get_error_digest() → GET /api/logs/error-digest) — dobry, świeży przykład do skopiowania przy kolejnych dodatkach.
Cache (`class-bot-cache.php` / `BOT_Cache`)
WordPress Transients z TTL per typ danych: TTL_STATS=600s, TTL_ALLEGRO_STATS=900s, TTL_CONFIG=3600s, TTL_CATEGORIES=86400s, TTL_METRICS=1800s. Wzorzec użycia: BOT_Cache::get($key, function() { ...fetch... }, $ttl). Nie każdy handler musi z tego korzystać — np. "Podsumowanie błędów" świadomie NIE cache'uje, bo świeżość ważniejsza od wydajności przy debugowaniu.
Kluczowe hooki WooCommerce
| Hook WooCommerce | Akcja |
|---|---|
woocommerce_order_status_processing | Planuje pobranie faktury z Fakturowni (60s opóźnienie) |
woocommerce_order_status_completed | Pobiera fakturę + upload do Allegro (jeśli zamówienie Allegro) |
woocommerce_order_status_changed → completed | Wysyła fulfillment do Allegro: completed → SENT |
woocommerce_new_order_item | Kopiuje _inpost_parcel_size z produktu do line item |
Meta box etykiety (`class-bot-labels.php`)
Wyświetlany na stronie zamówienia WC. Zawiera:
- Status etykiety z kolorowym źródłem (🟠 Allegro / 🟡 ShipX)
- Kurier + numer tracking z linkiem śledzenia
- Przyciski: Generuj / Pobierz PDF / Regeneruj
- Badge statusu trackingu (zielony delivered, niebieski w transporcie, czerwony zwrot)
- Sekcja Empik: OR23 ✅/❌, OR24 ✅/❌, przycisk Wyślij OR24
- Sekcja Fakturownia: link do faktury PDF
Jak sprawdzić, czy lokalna kopia jest aktualna
wc -l wordpress-plugin/bossoftoys-manager/includes/class-bot-admin.php # Oczekiwane: ~3800 linii. Jeśli wynik jest radykalnie mniejszy (np. ~500) — # kopia lokalna jest nieaktualna. Zatrzymaj się i poproś użytkownika o świeże pliki # zanim cokolwiek edytujesz — patrz incydent w rozdziale "Znane problemy", punkt 14.
API REST — mapa endpointów
Wszystkie endpointy wymagają nagłówka X-API-Key: {API_SECRET_KEY}, poza dwoma wyjątkami: GET /health (bez żadnej autoryzacji) i POST /api/webhooks/wc/new-order (webhook z WooCommerce — zamiast X-API-Key opcjonalnie weryfikowany podpisem HMAC-SHA256 w nagłówku X-WC-Webhook-Signature, jeśli skonfigurowano webhooks.wc_order_secret; bez ustawionego sekretu żądanie przechodzi bez weryfikacji).
Dla dokładnych ścieżek, parametrów i modeli request/response zawsze korzystaj z żywej dokumentacji uruchomionego serwera — GET /docs (Swagger UI) lub GET /redoc/GET /openapi.json. Ta tabela to mapa orientacyjna (rodzaje i skala endpointów, stan zweryfikowany 2026-07-17 bezpośrednim zliczeniem w kodzie) — .claude/API_DOCS.md opisuje tylko historyczną wersję 3.2.0 (ok. 18 endpointów Allegro) i nie nadąża za obecną skalą.
Routery i skala endpointów (`api/server.py::include_router`)
| Plik routera | Prefiks | Liczba endpointów | Funkcja |
|---|---|---|---|
api/routes/allegro.py | /api/allegro/* | ~106 | Cała integracja Allegro: OAuth, skan/listing, oferty, zamówienia, wiadomości, finanse, wysyłka, promocje, eventy, bundling, GPSR, kategorie, parametry, profile cenowe |
api/routes/config.py | /api/config/* | 16 | Konfiguracja modułów/sekcji |
api/routes/jobs.py | /api/jobs/* | 9 | Uruchamianie/status/anulowanie/logi zadań (generyczny dispatcher dla 31 typów modułów) |
api/routes/labels.py | /api/labels/* | 12 | Etykiety ShipX/Allegro, Empik OR23/OR24, pickupy |
api/routes/stats.py | /api/stats/* | 9 | Statystyki dashboardu, status schedulera |
api/routes/categories.py | /api/categories/* | 5 | Kategorie WooCommerce↔Allegro |
api/routes/returns.py | /api/returns/* | 5 | Zwroty przesyłek |
api/routes/sse.py | /api/sse/* | 3 | Server-Sent Events (logi/dashboard live) |
api/routes/params.py | /api/params/* | 3 | Parametry ofert Allegro (preview/apply) — uwaga: prefiks to /api/params, nie /api/allegro/params (zweryfikowane bezpośrednio w APIRouter(prefix=...), starsza referencja w skillu projektu podawała to błędnie) |
api/routes/logs.py | /api/logs/* | 3 | Logi plikowe serwera + GET /api/logs/error-digest (podsumowanie błędów per moduł, zdeduplikowane, dla Dashboardu WP) |
api/routes/webhooks.py | /api/webhooks/* | 1 | POST /wc/new-order — webhook z WooCommerce (nieujęty w schemacie /docs) |
Endpointy GET /api/metrics, GET /api/status, GET /api/modules, GET /health, GET / są zadeklarowane bezpośrednio w api/server.py, nie w api/routes/.
Przykładowe, często używane endpointy
| Metoda | Endpoint | Opis |
|---|---|---|
| POST | /api/jobs/start | Uruchom moduł: {"module": "stock-sync", "params": {}} |
| GET | /api/jobs/{job_id} | Status zadania |
| GET | /api/jobs/{job_id}/logs | Logi zadania |
| POST | /api/jobs/{job_id}/stop | Zatrzymaj zadanie |
| POST | /api/labels/generate/{order_id} | Generuj etykietę dla zamówienia |
| GET | /api/labels/download/{order_id} | Pobierz PDF etykiety |
| POST | /api/labels/empik/or23/{order_id} | Wyślij OR23 do Empika (tracking) |
| POST | /api/labels/empik/confirm-ship/{order_id} | Wyślij OR24 do Empika (SHIPPED) |
| GET | /api/allegro/auth-url | URL do autoryzacji OAuth |
| GET | /api/allegro/status | Status połączenia |
| GET | /api/allegro/offers | Lista ofert (paginacja, filtry) |
| PATCH | /api/allegro/offers/{id}/price | Zmień cenę oferty |
| POST | /api/allegro/offers/bulk-price-update | Zbiorcza zmiana cen (%) |
| GET | /api/allegro/orders | Lista zamówień |
| POST | /api/allegro/orders/{id}/fulfillment | Wyślij fulfillment |
| POST | /api/allegro/orders/{id}/invoices | Upload faktury PDF do Allegro (realna ścieżka — nie moduł allegro_invoice_upload.py, patrz rozdział Allegro) |
| GET | /api/allegro/messages/threads | Wątki wiadomości |
| POST | /api/allegro/messages/threads/{id}/reply | Odpowiedz w wątku |
| GET | /api/allegro/issues | Dyskusje i reklamacje (beta.v1) |
| GET | /api/allegro/billing | Rozliczenia |
| GET | /api/allegro/profitability | Rentowność ofert |
| GET | /api/allegro/delivery-services | Usługi dostawy (diagnostyka) |
| POST | /api/allegro/shipping/pickups | Zamów odbiór paczek |
| POST | /api/allegro/shipping/shipments/cancel | Anuluj przesyłki |
| POST | /api/allegro/shipping/protocol | Protokół nadania PDF |
| GET | /api/allegro/categories-tree | Drzewo kategorii WC (dla profili cenowych) — patrz rozdział Znane problemy, punkt 4 |
| POST | /api/allegro/tier-profiles/import-legacy | Import istniejących przedziałów jako nowy profil |
| GET | /api/stats/scheduler | Status VPS Schedulera |
| GET | /api/config/wp/test | Diagnostyka połączenia z WordPress REST API |
| GET | /api/logs/error-digest | Podsumowanie błędów per moduł (nowość 2026-07-18) |
Uruchomienie i pierwsze kroki
- Domyślnie serwer nasłuchuje na
0.0.0.0:8000:python run_api.py(opcje:--port,--host,--reload) - Dokumentacja interaktywna:
http://host:8000/docs(Swagger) i/redoc - Health check:
http://host:8000/health - Przy pierwszym uruchomieniu bez skonfigurowanego
API_SECRET_KEYserwer wygeneruje i jednorazowo wyświetli w konsoli nowy klucz API — trzeba go zapisać, nie zostanie pokazany ponownie.
Przepływ zamówień
Jak system rozpoznaje typ zamówienia?
Meta zamówienia WC (sprawdzane w tej kolejności):
├─ _allegro_checkout_id JEST → Zamówienie ALLEGRO
│ Etykieta: Allegro Shipment Management
│ Tracking: Allegro Tracking API
│ Faktura: upload do Allegro
│ Status: sync → Allegro SENT
│
├─ _empik_order_id JEST → Zamówienie EMPIK
│ Etykieta: ShipX (InPost)
│ OR23: numer trackingu do Empika (inline po etykiecie)
│ OR24: potwierdzenie wysyłki (CRON shipment-tracking)
│
├─ _ceneo_order_guid JEST → Zamówienie CENEO KUP TERAZ
│ Etykieta: ShipX (InPost)
│ SetOrderShipment: numer trackingu (inline po etykiecie)
│ SendOrder: potwierdzenie wysyłki (CRON shipment-tracking)
│
└─ Brak powyższych → Zamówienie SKLEPOWE
Etykieta: ShipX (InPost)
Tracking: ShipX API
Faktura: tylko lokalna (Fakturownia, bez uploadu do marketplace)Zamówienie sklepowe — pełny flow
KROK 1 — ZŁOŻENIE
Klient → WooCommerce → status: processing (po płatności)
PHP hook → planuje pobranie faktury (60s)
Geowidget → _inpost_point_id
PHP hook → _inpost_parcel_size z produktów do line items
KROK 2 — ETYKIETA (CRON: label-generator, co 10 min)
Brak _allegro_checkout_id → ShipX API
Odczytuje _inpost_parcel_size → największy gabaryt
Tworzy przesyłkę ShipX → PDF → data/labels/label_{id}.pdf
Meta: _shipx_shipment_id, _shipx_tracking_number
[auto_forward=true] → BossOfToys + status completed
KROK 3 — ŚLEDZENIE (CRON: shipment-tracking, co 120 min)
ShipX API → status paczki
"collected_from_sender" → AUTO-COMPLETE WC
PHP hook (completed) → Fakturownia PDF
GOTOWE ✅Zamówienie Allegro — pełny flow
KROK 1 — IMPORT (CRON: allegro-order-sync, co 10 min, hours_back=24)
Allegro API → POST /wc/v3/orders
Drugi PUT: _wc_order_attribution_utm_source="Allegro"
Meta: _allegro_checkout_id, _allegro_buyer_*, _allegro_delivery_*
PHP fix: zeruje VAT "zw" na wysyłce
KROK 2 — ETYKIETA (CRON: label-generator, co 10 min)
Jest _allegro_checkout_id → Allegro Shipment Management
3-krokowy flow: create → poll → label PDF
Smart wymiary z _inpost_parcel_size
Meta: _allegro_shipment_id, _allegro_tracking_number, _allegro_carrier
Pickup scheduling: DHL → zamów, InPost/GLS/UPS → skip
[auto_forward=true] → BossOfToys
KROK 3 — ŚLEDZENIE (CRON: shipment-tracking, co 120 min)
Allegro Tracking API (/shipment-management/shipments/{id})
statuses[-1] = najnowszy status
IN_TRANSIT → WC: "shipped"
DELIVERED → WC: "completed" → Fakturownia → upload faktury
→ Fulfillment sync: Allegro SENT
GOTOWE ✅Zamówienie Empik
KROK 1 — IMPORT Empik (Mirakl) → WooCommerce przez http_trigger import_orders (meta _empik_order_id) KROK 2 — ETYKIETA + OR23 (label-generator) ShipX etykieta → _shipx_shipment_id, _shipx_tracking_number OR23 inline: empik_client.send_tracking() → _empik_carrier_tracking_number KROK 3 — OR24 (shipment-tracking, co 120 min) "collected_from_sender" + _empik_order_id + tracking + brak or24_sent → OR24: empik_client.confirm_ship() → Empik: SHIPPED → _empik_or24_sent = "1" (ręczny fallback: przycisk "Wyślij OR24") GOTOWE ✅
Zamówienie Ceneo Kup Teraz
KROK 1 — IMPORT Zamówienie trafia do WC z meta _ceneo_order_guid (mechanizm importu poza core/ — sprawdź WP plugin/zewnętrzną integrację, jeśli to zadanie dotyczy) KROK 2 — ETYKIETA + SetOrderShipment (label-generator, inline) ShipX etykieta → _shipx_shipment_id, _shipx_tracking_number SetOrderShipment inline: ceneo_client.CeneoClient(...).set_order_shipment() → _ceneo_tracking_sent KROK 3 — SendOrder (shipment-tracking, co 120 min, inline) "collected_from_sender" + _ceneo_order_guid + brak _ceneo_send_order_sent → SendOrder → _ceneo_send_order_sent GOTOWE ✅
Harmonogram CRON
Zawartość zweryfikowana bezpośrednio w config/scheduler.json produkcyjnym (2026-07-18). Wszystkie zadania jobs są dziś enabled: true.
| Moduł | Interwał | Parametry | Priorytet |
|---|---|---|---|
| Label Generator | 10 min | limit=20 | ⭐⭐⭐ Krytyczny |
| Allegro Order Sync | 10 min | hours_back=24 | ⭐⭐⭐ Krytyczny |
| Allegro Fulfillment Sync | 10 min | — | ⭐⭐⭐ Krytyczny |
| Order Forwarder | 15 min | dry_run=false — realnie wysyła zamówienia! | ⭐⭐⭐ Krytyczny |
| Order Notifier | 5 min | — | ⭐⭐ Ważny |
| Allegro Messaging Sync | 5 min | — | ⭐⭐ Ważny |
| Stock Sync | 30 min | — | ⭐⭐ Ważny |
| Allegro Offer Sync | 30 min | — | ⭐⭐ Ważny |
| Boss Packing Monitor | 30 min | — | ⭐⭐ Ważny |
| Allegro Health Monitor | 60 min | — | ⭐ Normalny |
| Shipment Tracking | 120 min | status=processing,shipped | ⭐ Normalny |
| Price Updater | 24h | — | ⭐ Normalny |
| Empik Price Sync | 24h | — | ⭐ Normalny |
| Backup | 24h | — | ⭐ Normalny |
| Fakturownia Lump Sum Fix | 24h | days_back=7 | ⭐ Normalny |
Product Deleter nie ma dziś wpisu w config/scheduler.json (poprzednia wersja tej dokumentacji podawała go jako job dzienny z dry_run=true — nie potwierdzone w aktualnym pliku produkcyjnym). Uruchamiaj go ręcznie z panelu WordPress lub przez POST /api/jobs/start, zawsze najpierw z dry_run=true przez kilka dni, zanim przełączysz na realne usuwanie.
Zewnętrzne wyzwalacze HTTP (`http_triggers`)
Osobna sekcja w config/scheduler.json — VPS Scheduler cyklicznie wywołuje GET-y do WordPressa (nie do własnego API), głównie dla integracji Empik przez wtyczkę empik-for-woocommerce: eksport ofert (co 30 min), import zamówień (co 10 min), eksport produktów (co 24h). URL-e zawierają sekret w query string — patrz ostrzeżenie w rozdziale Konfiguracja.
Kolejność włączania (nowe wdrożenie)
- 1Etykiety i śledzenieLabel Generator + Shipment Tracking + Order Forwarder (lub
auto_forward=true) - 2Import zamówień z marketplace'ówAllegro Order Sync + Allegro Offer Sync; import Empik/Ceneo wg ich własnych mechanizmów
- 3ProduktyStock Sync + Price Updater + Product Adder (najpierw raz ręcznie)
- 4Product DeleterNajpierw ręcznie z
dry_run=trueprzez kilka dni, sprawdź logi, dopiero potem realne usuwanie
Meta dane na zamówieniach WooCommerce
Kluczowe meta (routing systemu)
| Klucz meta | Wartość | Znaczenie |
|---|---|---|
_allegro_checkout_id | UUID | Zamówienie pochodzi z Allegro → Allegro flow |
_empik_order_id | string | Zamówienie pochodzi z Empika → Empik flow |
_ceneo_order_guid | string | Zamówienie pochodzi z Ceneo Kup Teraz → Ceneo flow |
_shipping_label_source | "allegro" / "shipx" | Skąd pochodzi etykieta |
_inpost_point_id | string | ID paczkomatu docelowego (z Geowidgetu) |
_inpost_parcel_size | a/b/c/courier | Gabaryt paczki (na produkcie i skopiowany na line item) |
_boss_packed_at | ISO 8601 lub pusty | Zamówienie potwierdzone jako spakowane przez BossOfToys (Boss Packing Monitor, IMAP) |
_own_stock_handled | "1" | Zamówienie w 100% z własnego towaru (zwrotu) — NIE przekazane do BossOfToys. Ustawiane przez own_stock.py (nowość 2026-07-20) |
Meta produktu (nie zamówienia) sterujące tym mechanizmem: _own_stock_item = yes/no, checkbox w zakładce Zapasy karty produktu. Szczegóły: rozdział Moduły — BossOfToys / WooCommerce, sekcja "Własny towar / zwroty".
Meta etykiet Allegro
| Klucz | Opis |
|---|---|
_allegro_shipment_id | ID przesyłki Allegro Shipment Management |
_allegro_tracking_number | Numer śledzenia przesyłki |
_allegro_carrier | Nazwa przewoźnika (DHL, InPost, DPD…) |
_allegro_label_id | ID etykiety Allegro |
_allegro_label_created | Timestamp wygenerowania |
_allegro_pickup_scheduled / _allegro_pickup_command_id | Czy i jakim poleceniem zamówiono podjazd kuriera |
_allegro_shipment_status / _allegro_shipment_status_label | Aktualny status (IN_TRANSIT, DELIVERED…) i jego czytelna etykieta PL |
_allegro_invoice_uploaded | Czy faktura została wysłana do Allegro |
Meta etykiet ShipX
| Klucz | Opis |
|---|---|
_shipx_shipment_id | ID przesyłki ShipX |
_shipx_tracking_number | Numer śledzenia |
_shipx_status | Aktualny status ShipX |
_shipx_status_label | Czytelna etykieta PL |
_shipx_status_updated | Timestamp ostatniej aktualizacji |
Meta Empik
| Klucz | Opis |
|---|---|
_empik_order_id | ID zamówienia Empik Mirakl |
_empik_carrier_tracking_number | Numer trackingu (po OR23) |
_empik_or24_sent | "1" po potwierdzeniu wysyłki (OR24) |
Meta Ceneo
| Klucz | Opis |
|---|---|
_ceneo_order_guid | GUID zamówienia Ceneo Kup Teraz |
_ceneo_tracking_sent | Czy SetOrderShipment (odpowiednik OR23) został wysłany |
_ceneo_send_order_sent | Czy SendOrder (odpowiednik OR24) został wysłany |
Meta Attribution (Pochodzenie w WC)
| Klucz | Wartość | Efekt |
|---|---|---|
_wc_order_attribution_utm_source | "Allegro" / "Import z Empik" | Wartość w kolumnie "Pochodzenie" |
_wc_order_attribution_source_type | "utm" | Wymagane — bez tego WC nie wyświetla źródła |
Zależności między modułami
BossOfToys API / XML
│
├──────────────────────────────────┐
▼ ▼
Stock Sync ──────────► WooCommerce Product Adder
Price Updater ─────────► (REST API) Product Deleter
Cost Backfill │
│
┌──────────────────────┤
│ │
▼ ▼
Allegro Scanner ──► WooCommerce produkty (EAN)
│
▼
Allegro Lister ──────────► Allegro API (OAuth 2.0)
│
┌────────────────────────────┤
│ │
▼ ▼
Allegro Order Sync Allegro Offer Sync
│ (+ Markup/Tier Profiles)
▼ │
WooCommerce ▼
(zamówienia, Allegro oferty
+ Empik/Ceneo (stany/ceny)
przez własne wejścia)
│
├──────────────────────────────────────┐
│ │
▼ ▼
Label Auto Generator Order Forwarder
│ │ │ │
│ │ │ ▼
▼ ▼ ▼ BossOfToys API
ShipX Allegro inline OR23/ (zamówienie + PDF)
Label Label SetOrderShipment
│ │ (Empik / Ceneo)
└──┬───┘
│
▼
Shipment Tracking ──► ShipX API / Allegro Tracking API
│
├──────────────► AUTO-COMPLETE WC
├──────────────► OR24 Empik / SendOrder Ceneo (inline, jeśli meta obecne)
└──────────────► Fakturownia → upload faktury → Allegro SENTKolejność uruchamiania modułów
- Label Generator — etykiety muszą być pierwsze (dla wszystkich kanałów)
- Shipment Tracking — śledzi i auto-complete/OR24/SendOrder
- Order Forwarder — wymaga istniejącej etykiety
- Allegro Order Sync — import nowych zamówień
- Stock Sync — stany magazynowe
- Allegro Offer Sync — po sync stanów
- Price Updater — ceny
- Allegro Fulfillment Sync — na końcu (zależy od etykiet i statusów)
Bezpieczeństwo
Ten rozdział konsoliduje ustalenia bezpieczeństwa z .claude/SECURITY_SETUP.md (historyczny opis Windows Server) i audytu kodu z 2026-07-17/18. Koncepcja "dwuwarstwowego zabezpieczenia" (VPN mesh + whitelist IP) jest nadal dobrą praktyką, ale jej konkretna implementacja opisana w starym dokumencie dotyczy poprzedniego serwera Windows i wymaga potwierdzenia na obecnym VPS Linux.
Model sieciowy — do zweryfikowania na obecnym VPS
- Historycznie: Tailscale (mesh VPN, WireGuard) dla dostępu administracyjnego + Windows Firewall whitelist IP dla dostępu z serwera WooCommerce. Tailscale działa też natywnie na Linuksie (
tailscaled+ systemd) — jeśli nadal jest używany, komendy typutailscale statusdziałają identycznie. docker-compose.ymlpublikuje port8000:8000, co domyślnie wystawia port na wszystkich interfejsach hosta, o ile firewall hosta lub reverse proxy tego nie ogranicza. Nie zakładaj, że ochrona nadal działa tak samo jak w opisie Windows — zweryfikuj z użytkownikiem aktualną konfigurację firewalla/reverse proxy na obecnym VPS przed poleganiem na tym modelu.- Skrypty PowerShell (
firewall_setup.ps1,firewall_rollback.ps1,firewall_add_ip.ps1) opisane w starym dokumencie nie istnieją już w repo — albo świadomie usunięte przy migracji, albo nigdy nie trafiły do tego katalogu roboczego.
Znane, potwierdzone ryzyka
| Ryzyko | Szczegóły | Status |
|---|---|---|
| Transport WordPress → VPS bez TLS | class-bot-api.php ma jawnie 'sslverify' => false — cała komunikacja (w tym X-API-Key) leci zwykłym HTTP. Bezpieczeństwo opiera się wyłącznie na warstwie sieciowej. | Do potwierdzenia z użytkownikiem, czy świadome |
config/settings.json.template zawiera realne sekrety | Mimo nazwy "template", plik zawiera klucz/secret WooCommerce i hasło do API BossOfToys wyglądające jak prawdziwe dane produkcyjne, nie placeholdery. Jeśli plik trafił kiedyś do repozytorium git, sekrety mogły wyciec do historii commitów. | Do rotacji/weryfikacji |
Szyfrowanie settings.dat kluczem z adresu MAC | uuid.getnode() w secure_config.py. Błąd deszyfrowania jest cichy — config staje się pusty bez crasha. Dane wrażliwe są bezpieczne (żyją w .env), ryzyko dotyczy tylko ustawień niewrażliwych. | Wiedza operacyjna — czujność przy migracjach |
| Ten katalog roboczy nie jest repozytorium git | Mimo że dokumentacja projektu odwołuje się do github.com/RadoslawSwider/RSJB-Bossoftoys, lokalny katalog nie ma zainicjalizowanego .git. Zmiany w plikach nie trafiają automatycznie do faktycznego repozytorium/serwera — wymaga ręcznej synchronizacji. | Do ustalenia z użytkownikiem |
Autoryzacja API
- Wszystkie endpointy poza
/healthi/wymagają nagłówkaX-API-Keyzgodnego zAPI_SECRET_KEYz.env. - Tokeny OAuth Allegro żyją w SQLite (
kv_store), nie w plikach configu — nie sprawdzaj obecnościaccess_tokenw configu jako testu autoryzacji, tylkoclient_id/client_secret. http_triggerswconfig/scheduler.json(integracja Empik) używają sekretu wprost w query string URL — traktuj te URL-e jak hasło, nigdy nie wklejaj ich do dokumentów/logów/zgłoszeń.
Dodatkowe zabezpieczenia wskazane w starym dokumencie jako "do rozważenia w przyszłości" (i wciąż aktualne): certyfikat TLS między WordPress a VPS, rate limiting, mechanizm typu fail2ban dla portu API.
Znane problemy i ryzyka
Skonsolidowana lista z .claude/KNOWN_ISSUES.md (stan 2026-07-18) — nie jest to lista potwierdzonych, aktualnie trwających awarii, tylko udokumentowanych ryzyk i incydentów z pełną historią diagnozy. Pełne szczegóły techniczne (dowody, dokładne linie kodu) w źródłowym pliku.
| # | Problem | Status |
|---|---|---|
| 1 | Logi (data/logs/) nigdy się nie czyściły — zły wzorzec glob w cleanup_old_log_files() (kropki zamiast myślników w nazwie pliku) | Naprawione i wdrożone 2026-07-18, niepotwierdzone wprost przez użytkownika że retencja realnie czyści; istniejący narosły balast (1.4 GB w kopii audytowej) wymaga jednorazowego ręcznego czyszczenia |
| 2 | allegro-messaging-sync/allegro-events-poll — błąd sygnatury on_finish_callback() got an unexpected keyword argument 'job_id' | Naprawione w punkcie kontraktu (api/worker.py przyjmuje teraz *args, **kwargs), wdrożone 2026-07-18, niepotwierdzone wprost przez użytkownika po realnym wdrożeniu |
| 3 | Order Forwarder nie widział świeżo wygenerowanej etykiety (auto-forward) — klasyczny problem "write-then-read" po stronie WooCommerce REST API (prawdopodobnie cache hostingu) | Naprawione (etykieta uzupełniana lokalnie ze znanych danych, nie tylko ze świeżego GET), wdrożone 2026-07-18, niepotwierdzone wprost przez użytkownika |
| 4 | Profile przedziałów cenowych Allegro — "Błąd: not found" / "Błąd pobierania kategorii WooCommerce". Przy okazji znaleziono poważniejszy problem: błędy pobierania kategorii WC były całkowicie wyciszane (_silent_logger), więc niediagnozowalne z logów serwera z zasady | Naprawione i POTWIERDZONE przez użytkownika 2026-07-18 — przyczyną był najpewniej nieodświeżony obraz Dockera (patrz punkt 12), nie kod aplikacji |
| 5 | storage/temp/*.xml (cache XML z BossOfToys, ~48 MB/plik) — brak widocznej logiki czyszczenia | Do obserwacji — niższe ryzyko niż logi, ale kolejny potencjalny wektor zapełnienia dysku |
| 6 | WordPress → VPS to zwykłe HTTP, nie HTTPS (sslverify => false) | Ryzyko bezpieczeństwa — patrz rozdział Bezpieczeństwo |
| 7 | config/settings.json.template zawiera dane wyglądające jak realne sekrety zamiast placeholderów | Ryzyko bezpieczeństwa — do rotacji/weryfikacji |
| 8 | settings.dat szyfrowany kluczem z adresu MAC — cicha utrata ustawień niewrażliwych przy migracji sprzętu/kontenera bez pinowanego mac_address | Wiedza operacyjna — nic do zrobienia teraz, czujność na przyszłość |
| 9 | Plan multi-account Allegro (10 znanych błędów architektonicznych, B1-B10) — w pełni opisany, nic nie wdrożone | Praca do zaplanowania osobno, gdy będzie na czasie |
| 10 | Duże historyczne rozjazdy dokumentacja ↔ kod (częściowo naprawione audytem 2026-07-17/18, w tym ta dokumentacja) | W toku — dokumentacja bywa w tyle za kodem; traktuj ją jako punkt startowy, nie ostateczne źródło prawdy |
| 11 | Numeracja migracji DB w planach (np. planie multi-account) jest nieaktualna względem faktycznego api/migrations.py | Do pamiętania — zawsze używaj len(MIGRATIONS) + 1, nie numeru z dokumentu |
| 12 | Brak Dockerfile w repo — wolumeny docker-compose.yml nie obejmowały api//core/, więc wgrywany kod nigdy nie docierał do działającego kontenera. Wyjaśnia całą serię "poprawka nie działa po redeployu" (punkty 1-4) | ROOT CAUSE naprawiony 2026-07-18 (dodano wolumeny ./api:/app/api, ./core:/app/core) — nadal otwarte: gdzie/jak buduje się obraz bossoftoys-api:latest |
| 13 | Ten katalog roboczy nie jest zainicjalizowanym repozytorium git | Do ustalenia z użytkownikiem — nie wynika stąd bug, ale zmiany wymagają ręcznej synchronizacji z faktycznym repo/serwerem |
| 14 | Incydent (rozwiązany): skasowana zakładka Allegro w WordPress 2026-07-18 — edycja pluginu na bazie drastycznie nieaktualnej lokalnej kopii repo nadpisała pełne, żywe pliki produkcyjne | ROZWIĄZANE — użytkownik dostarczył świeży backup, stan odtworzono, dodano trwałe zabezpieczenie proceduralne (references/wordpress-plugin.md + zasada weryfikacji rozmiaru plików przed edycją) |
| 15 | Incydent (rozwiązany): allegro_category_sync.py::run() uruchomiony kiedyś na żywo nadpisał pole categories produktów wystawionych na Allegro kategorią z taksonomii Allegro zamiast hurtowni — 11 475/19 639 produktów (58%) miało złą kategorię, a drzewo kategorii miało 139 osieroconych, całkowicie martwych kategorii (0 produktów na każdym poziomie poddrzewa) z ogólnej taksonomii Allegro (np. "Części motocyklowe", "Wędkarstwo") | ROZWIĄZANE 2026-07-22 — scripts/rebuild_categories_from_boss.py skorygował wszystkie 11 475 produktów (0 błędów, zero strat SEO), a 139 martwych kategorii skasowano po potwierdzeniu użytkownika. Patrz rozdział Moduły Allegro |
Wciąż niepotwierdzone wprost przez użytkownika (stan 2026-07-18): czy błędy #1 (rotacja logów), #2 (on_finish_callback/job_id) i #3 (order-forwarder bez etykiety) faktycznie przestały się powtarzać na produkcji po realnym wdrożeniu. Najszybszy sposób sprawdzenia: sekcja "Podsumowanie błędów" w Dashboardzie WordPress (GET /api/logs/error-digest, dodana 2026-07-18).
Historia wersji
.claude/CHANGELOG.md urywa się merytorycznie na wpisach do 3.30.0/3.33.0 plus jeden wpis audytowy z 2026-07-17 — nie odnotowuje wprost dodania Ceneo, modułu zwrotów, profili narzutów/przedziałów cenowych ani usunięcia modułu repricingu. Tabela niżej łączy potwierdzone daty z changeloga z ustaleniami z bezpośredniego audytu kodu (2026-07-17/18); tam gdzie dokładna data wprowadzenia nie jest znana, jest to zaznaczone wprost zamiast zgadywane.
| Wersja / data | Kluczowe zmiany |
|---|---|
| 2026-07-22 | Naprawiony incydent: allegro_category_sync.py nadpisywał kategorie WC produktów wystawionych na Allegro. Skorygowano 11 475 produktów jednorazowym skryptem scripts/rebuild_categories_from_boss.py (zero strat SEO). Dodano checkbox "Nie wystawiaj na Allegro" (_allegro_exclude, sprawdzany w allegro_scanner.py). Współdzielona logika kategorii wydzielona do core/category_utils.py. BOT_VERSION → 3.26.0. |
| 2026-07-21 | Dropdown filtra "Tylko własny towar" nad listą produktów WP (class-bot-own-stock.php). |
| 2026-07-20 | Nowy mechanizm "Własny towar" (own_stock.py) — odsprzedaż zwrotów wyłącznie w sklepie, bez przekazywania zamówienia do BossOfToys. Checkbox na karcie produktu, ochrona w product_deleter_woo.py, widoczność (NTFY + badge na liście zamówień + ostrzeżenie w meta boxie). Usunięty stary, nieużywany mechanizm home_stock_qty. BOT_VERSION → 3.25.0. |
| 2026-07-18 | ROOT CAUSE naprawiony: wolumeny Dockera dla api//core/. Naprawy: rotacja logów, sygnatura on_finish_callback, widoczność etykiety dla order-forwarder. Nowa funkcja "Podsumowanie błędów" w Dashboardzie (/api/logs/error-digest). Incydent i odzyskanie pluginu WordPress (zakładka Allegro). Ta dokumentacja przepisana na bazie pełnego audytu. |
| 2026-07-17 | Audyt architektury + nowy skill programisty erotivo-dev + aktualizacja dokumentacji pod Linux (bez zmian w kodzie produkcyjnym poza wstępnymi poprawkami wymienionymi wyżej) |
| nieznana data, przed 2026-07-11 | Profile narzutów i przedziałów cenowych Allegro (markup_profiles, tier_profiles, tier_tables — migracje 14-16); moduł zwrotów przesyłek (returned_shipments, return_notes — migracje 12-13); integracja Ceneo Kup Teraz; usunięcie modułu allegro_repricing.py (Allegro zablokowało dostęp do API cen konkurencji). Żadna z tych zmian nie ma wpisu w CHANGELOG.md — daty ustalone pośrednio (rekordy w bazie z 2026-07-11). |
| 3.33.0 · 2026-05-21 | Usunięcie GUI Desktop (PyQt6), moduł etykiet API-only, allegro_category_sync + allegro_params_sync, boss_api_client, fakturownia_lump_sum_fix |
| 3.32.0 · 2026-04-22 | Empik Marketplace (OR23/OR24), Allegro order attribution fix, strona logów |
| 3.31.0 · 2026-03 | Webhook Circuit Breaker, Allegro Offer Sync rozszerzony, stabilność VPS Schedulera |
| 3.30.0 · 2026-03-07 | Allegro Delivery (Shipment Management API), Dual Shipment Tracking, Smart wymiary InPost, Allegro Pickup Scheduler |
| 3.29.0 · 2026-02-11 | Shipment Tracking (ShipX), Auto-Forward do BossOfToys, NTFY powiadomienia o błędach etykiet |
| 3.27.0 · 2026-02-13 | Migracja JSON → SQLite (11 tabel na starcie, Repository pattern, adapter data_store) |
| 3.26.0 · 2026-02-12 | Labels v2 (PDF na dysku VPS), deduplikacja przesyłek, wykrywanie przewoźnika |
| 3.20.0 · 2026-02 | VPS Scheduler (zastępuje WP-Cron) |
| 3.8.1 · 2026-02 | Fakturownia.pl (faktury + produkty), upload faktur do Allegro |
| 3.7.0 · 2026-02-04 | Issues API beta.v1, UI/UX refresh panelu Allegro, finanse |
| 3.2.0 · 2026-01-31 | Allegro Integration v2.0 (GPSR, dryRun, obrazy, opisy, sync, fulfillment) |
| 3.1.0 · 2026-01 | BossOfToys REST API (ceny z rabatami) |
| 3.0.0 · 2026-01 | WordPress Bridge, Dashboard, telemetria, Chart.js — początek architektury API+plugin (wcześniej: aplikacja desktopowa PyQt6) |
Rozwiązywanie problemów
Diagnoza ogólna — pierwsze kroki
- Sprawdź logi VPS: WordPress → BeeIntegro → Logi, albo sekcję "Podsumowanie błędów" na Dashboardzie
- Status API Allegro:
GET /api/allegro/status - Diagnostyka WP REST API:
GET /api/config/wp/test - Status Schedulera:
GET /api/stats/scheduler - Zweryfikuj, że kontener faktycznie widzi aktualny kod:
docker compose exec bossoftoys-api grep -n "fragment zmiany" /app/api/plik.py - Weryfikacja składni:
python -m py_compile core/nazwa_modulu.py
Allegro
Brak tokenu / "Nie połączono z Allegro"
Sprawdź czy ALLEGRO_CLIENT_ID i ALLEGRO_CLIENT_SECRET są w .env. Tokeny OAuth są w SQLite (kv_store), nie w configu — autoryzuj przez WordPress → Ustawienia → Allegro.
Zamówienia Allegro — "Pochodzenie: Nieznane"
Wymaga obu meta: _wc_order_attribution_utm_source="Allegro" ORAZ _wc_order_attribution_source_type="utm". WooCommerce może nadpisać attribution w swoim hooku — allegro_order_sync.py wykonuje drugi PUT po zapisie zamówienia właśnie z tego powodu.
Moduł Repricing zwraca błąd/wyjątek
To oczekiwane — core/allegro_repricing.py jest dziś funkcjonalnie usunięty (rzuca RuntimeError) po tym jak Allegro zablokowało dostęp do API cen konkurencji na kontach produkcyjnych. To nie jest bug do naprawienia.
Błąd 406 przy pobieraniu etykiety
Header Accept: application/octet-stream jest wymagany (NIE application/pdf!).
Endpoint etykiety 404
/shipment-management/label/commands NIE ISTNIEJE. Używaj POST /shipment-management/label.
| Objaw | Rozwiązanie |
|---|---|
| Duplikaty przesyłek Allegro | Moduł sprawdza _allegro_shipment_id w meta. Duplikaty → anuluj przez panel WP → Allegro → Wysyłka → Anulowanie. |
| Pickup DHL się nie dzieje | Sprawdź allegro.pickup.carrier_overrides.DHL.auto_schedule=true. Błędy pickup schedulera są non-fatal — sprawdź logi, nie blokują reszty flow. |
| Oferta odrzucona — opis | Usuń tagi <br>. Dozwolone: <b> <i> <u>. |
| Profile przedziałów cenowych: "Błąd pobierania kategorii WooCommerce" | Sprawdź logi docker compose logs pod loggerem allegro_profiles (naprawiono wyciszanie błędów 2026-07-18). Najczęstsza przyczyna historyczna: nieodświeżony obraz Dockera bez najnowszych endpointów. |
WooCommerce / WordPress
| Objaw | Rozwiązanie |
|---|---|
| 403 przy requestach WP REST API | OVH LiteSpeed WAF blokuje python-requests. Wszystkie requesty do /wp-json/wp/v2/* muszą używać Chrome User-Agent. |
Klucze WC nie działają na /wp/v2/ | Normalne — WP REST API wymaga Application Password (WordPress → Użytkownicy → profil), nie kluczy WooCommerce. |
Błąd generator has no len() | get_orders_by_status() zwraca generator. Owijaj w list(): orders = list(woo_client.get_orders_by_status(...)) |
| Gabaryt InPost zawsze A | Sprawdź _inpost_parcel_size na produktach i czy PHP hook kopiuje meta do line items przy składaniu zamówienia. |
Zmiana w dashboard.js/admin.css "nie działa" mimo wgrania | Cache przeglądarki po ?ver={BOT_VERSION} — zbumpuj BOT_VERSION w bossoftoys-manager.php i/lub zrób twardy refresh (Ctrl+Shift+R). |
| Panel Allegro/etykiety "zniknęły" po edycji pluginu | Prawdopodobnie edycja na bazie nieaktualnej lokalnej kopii nadpisała pełne pliki produkcyjne. Przywróć z backupu, zweryfikuj rozmiar class-bot-admin.php (~3800 linii) przed kolejną edycją. |
Empik / Ceneo
| Objaw | Rozwiązanie |
|---|---|
| OR24 nie poszło automatycznie | Sprawdź czy OR23 poszedł (_empik_carrier_tracking_number w meta). CRON retryuje co 120 min. Użyj ręcznego przycisku "Wyślij OR24" jako fallback. |
| Ceneo SendOrder nie idzie | Sprawdź _ceneo_order_guid i CENEO_API_KEY w .env. Logika jest inline w shipment_tracking.py — sprawdź logi pod kątem wyjątku Ceneo SendOrder wyjątek. |
Baza danych
| Objaw | Rozwiązanie |
|---|---|
Circular import w core/ | Nigdy nie importuj api.repository w core/. Używaj from core.data_store import data_store — i tylko wewnątrz funkcji. |
| Stary schemat DB po aktualizacji | Migracje odpalają się automatycznie przy starcie serwera. Sprawdź logi startowe pod kątem [Migration NNN]. |
| Dane znikają po restarcie | SQLite jest trwałe. Sprawdź czy data/api_bridge.db jest poprawnie zamontowane jako wolumen i nie jest nadpisywane przy deployu. |
| "database is locked" po migracji na inny hosting | Sprawdź, czym faktycznie jest zamontowane ./data na hoście — sieciowe systemy plików (NFS itp.) potrafią psuć blokady SQLite WAL. |
Komendy diagnostyczne
# Weryfikacja składni Pythona python -m py_compile core/allegro_order_sync.py # Test połączenia Allegro curl -H "X-API-Key: TWOJ_KLUCZ" http://vps:8000/api/allegro/status # Test połączenia WordPress curl -H "X-API-Key: TWOJ_KLUCZ" http://vps:8000/api/config/wp/test # Status Schedulera VPS curl -H "X-API-Key: TWOJ_KLUCZ" http://vps:8000/api/stats/scheduler # Podsumowanie błędów per moduł (dodane 2026-07-18) curl -H "X-API-Key: TWOJ_KLUCZ" http://vps:8000/api/logs/error-digest # Weryfikacja, że kontener widzi aktualny kod z wolumenu docker compose exec bossoftoys-api grep -n "fragment" /app/api/plik.py # Rozmiar pluginu WP — test świeżości lokalnej kopii wc -l wordpress-plugin/bossoftoys-manager/includes/class-bot-admin.php
Zasady pracy nad kodem (dla programistów)
Ten projekt ma dojrzałe, spójne konwencje — poniższe zasady dotyczą pracy nad core/, api/ i wordpress-plugin/. Zebrane z .claude/skills/erotivo-dev/ i preferencji zapisanych w .claude/CLAUDE.md.
Twarde zasady projektu
- Polski język — komunikaty logów, komentarze, teksty UI po polsku.
- Stabilność ponad prędkość — długie timeouty, mniejsze batche, retry z backoff zamiast agresywnej równoległości.
- DRY-RUN domyślnie dla operacji destrukcyjnych (usuwanie produktów, przekazywanie zamówień). Nie usuwaj/nie osłabiaj trybu dry-run bez wyraźnej prośby.
- Nigdy nie commituj
.env,config/settings.dat,config/api_key.txt,data/— sekrety i dane runtime. - Weryfikacja składni Python:
python -m py_compile core/plik.py— nie ma testów jednostkowych w repo, to jedyna sieć bezpieczeństwa składniowa przed wdrożeniem. - Weryfikacja PHP:
php -l plik.php(jeśli PHP dostępne lokalnie; jeśli nie — policz nawiasy klamrowe ręcznie). Dla JS:node --check plik.js.
Wzorzec: jak dodać nowy moduł biznesowy
- 1Napisz moduł
core/nazwa_modulu.pyz funkcją wejściowądef run(config, logger, **kwargs) -> dict. Loguj przez przekazanylogger/callback — nie używajprint()wcore/. - 2Zarejestruj typW
api/models.py::ModuleType(Enum) dodaj nowy wpis. - 3Podłącz do dispatcheraW
api/worker.py::MODULE_MAP—ModuleType.NOWY: nazwa_modulu.run. Specjalnecall_params→ blok wJobExecutor. - 4CRON (opcjonalnie)Wpis w
config/scheduler.json— scheduler przeładowuje się automatycznie, bez restartu. - 5Dedykowany endpoint (opcjonalnie)Zamiast generycznego
POST /api/jobs/{module}— dodaj w odpowiednimapi/routes/*.py, zarejestruj router wapi/server.pyjeśli to nowy plik. - 6Trwały stan między uruchomieniamiUżyj
core.data_store.data_store, NIE osobnych plików JSON, chyba że moduł już historycznie tak robi. - 7Zaktualizuj dokumentację od razuNie zostawiaj tego na później — ten projekt ma historię dokumentacji zostającej daleko w tyle za kodem.
Dostęp do danych — nie omijaj warstw
core/*.py → core.data_store.data_store → api/repository.py → api/database.py → SQLite
Import from core.data_store import data_store zawsze lokalnie, wewnątrz funkcji, nigdy na górze pliku — świadoma konwencja, żeby uniknąć circular importów core↔api. Nigdy nie importuj api.repository bezpośrednio z core/.
Integracje zewnętrzne — gdzie szukać kontraktów API
| Integracja | Klient | Uwaga |
|---|---|---|
| WooCommerce | core/api_clients_woo.py (WooCommerceClient) | Konstruktor wymaga url, key, secret, logger= — brak logger to częsty błąd kopiowania kodu. Atrybut to .wcapi, nie .client. |
| BossOfToys | core/boss_api_client.py, core/api_clients_woo.py (BossoftoysAPIClient) | Znany bug API dostawcy: TotalItemsCount zawsze = page_size — paginacja musi iterować while len(page) == page_size, nie po TotalItemsCount. |
| Allegro | core/allegro_client.py | Patrz pełna lista pułapek w rozdziale Moduły Allegro. Multi-konto to plan, NIE implementacja. |
| ShipX (InPost) | core/shipx_client.py, core/shipx_label_generator.py | Osobna umowa od Allegro Delivery. |
| Empik (Mirakl) | core/empik_client.py | OR23 ≠ OR24 (tracking vs. potwierdzenie wysyłki). Plugin empik-for-woocommerce wrzuca kod paczkomatu w shipping.last_name. |
| Ceneo Kup Teraz | core/ceneo_client.py | Logika inline w label_auto_generator.py/shipment_tracking.py, nie osobny CRON job. |
| Fakturownia.pl | core/fakturownia_client.py | Auto-faktury po zmianie statusu zamówienia. |
WordPress plugin — dodatkowa ostrożność
Zanim zrobisz nieaddytywną zmianę w wordpress-plugin/ — zapytaj użytkownika, czy lokalna kopia jest aktualna, albo poproś o świeży plik/backup. Preferuj zmiany addytywne (nowa metoda/funkcja/sekcja dopisana obok istniejącego kodu, kotwiczona o stabilny, unikalny fragment tekstu) nad przepisywaniem całych plików od zera. Zob. incydent w rozdziale Znane problemy, punkt 14 — to nie jest zasada teoretyczna.
Deploy — dwie osobne procedury
Nie myl ich — pełne szczegóły w rozdziale Wdrożenie. W skrócie: backend Python (VPS/Docker) wymaga docker compose restart (plik .py) lub down && up -d (zmiana docker-compose.yml); plugin WordPress działa od razu po wgraniu FTP, ale wymaga bumpnięcia BOT_VERSION przy zmianach JS/CSS.
Zanim zaczniesz zmieniać kod — checklist
find core api -name "*.py"/ Glob — zweryfikuj, że moduł faktycznie tak się nazywa.- Sprawdź, czy podobny wzorzec już istnieje gdzie indziej w
core/(47 modułów = 47 przykładów konwencji) zamiast wymyślać nowy styl. - Dla zmian w
core/— pamiętaj o lazy importsdata_storei wymaganymlogger=wWooCommerceClient. - Dla zmian w Allegro — sprawdź listę pułapek API w rozdziale Moduły Allegro.
- Dla zmian w
wordpress-plugin/— zweryfikuj świeżość kopii (wc -l class-bot-admin.php~3800 linii) i preferuj zmiany addytywne. - Po zmianie:
python -m py_compile <plik>, a dla WordPress:php -l/policz nawiasy +node --checkdla JS. - Zapytaj użytkownika przed każdą operacją nieodwracalną/współdzieloną (deploy na VPS, restart kontenera produkcyjnego, zmiany na erotivo.pl) — to działający sklep.
Słowniczek pojęć i skrótów
| Termin | Znaczenie |
|---|---|
| OR23 / OR24 | Operacje Mirakl API (Empik): OR23 = wysłanie numeru trackingu, OR24 = potwierdzenie wysyłki (SHIPPED). Ceneo ma analogiczne SetOrderShipment/SendOrder. |
| GPSR | General Product Safety Regulation — unijne wymogi dot. danych producenta/importera na ofertach Allegro (m.in. producerData w payloadzie). |
| WAL (mode) | Write-Ahead Logging — tryb SQLite pozwalający na równoczesny odczyt i zapis bez blokowania całej bazy. |
| TTL | Time To Live — czas ważności wpisu w cache, po którym dane są uznawane za nieaktualne. |
| dryRun / DRY-RUN | Tryb symulacji — operacja jest logowana/liczona, ale nie wykonywana naprawdę. Domyślny dla operacji destrukcyjnych w tym projekcie. |
| Ghost tracking | Mechanizm śledzący, od kiedy produkt jest nieobecny u dostawcy, żeby po X dniach wyzerować stan lub usunąć produkt. |
| Fulfillment | W kontekście Allegro: proces informowania marketplace'u o statusie realizacji zamówienia (PROCESSING/SENT/CANCELLED). |
| Attribution (WC) | Mechanizm WooCommerce pokazujący "Pochodzenie" zamówienia w liście zamówień — sterowany meta _wc_order_attribution_*. |
| SSE | Server-Sent Events — jednokierunkowy strumień zdarzeń HTTP z serwera do przeglądarki, używany do logów live w Dashboardzie. |
| MODULE_MAP | Słownik w api/worker.py mapujący ModuleType (string z URL-a) na funkcję run() konkretnego modułu core/. |
| BeeIntegro | Nazwa handlowa/wyświetlana pluginu WordPress, którego katalog roboczy nazywa się historycznie bossoftoys-manager. |
| Geowidget | Widget InPost do wyboru paczkomatu docelowego na checkout WooCommerce. |
| Smart wymiary / gabaryt | Automatyczny dobór rozmiaru paczki InPost (A/B/C/kurier) na bazie meta produktów w zamówieniu — bierze największy z nich. |
| NTFY | Usługa powiadomień push (self-hosted lub ntfy.sh) używana do alertów krytycznych z VPS na telefon/desktop. |
| Checkpoint / resume | Mechanizm zapisywania postępu długotrwałej operacji (np. Product Adder), żeby po przerwaniu wznowić od ostatniego punktu zamiast zaczynać od zera. |